Laravel的路由設定非常簡單,但是在大型專案中需要配置很多路由資訊,顯得比較臃腫,可以對路由進行如下簡化:
首先建立一箇中介軟體 HttpDiscernMiddleware:
<?php
namespace App\Http\Middleware;
use Closure;
use Log;
use App;
class HttpDiscernMiddleware
{
protected $controller;
protected $method;
protected $api_version;
/**
* Request Method&Api Version Discern Middleware
* @param $request
* @param Closure $next
* @return mixed
* @throws \Exception
*/
public function handle($request, Closure $next)
{
$real_method = $request->getRealMethod();
$scheme_host = $request->getSchemeAndHttpHost();
$request_uri = $request->getRequestUri();
$client_ip = $request->getClientIp();
$content_type = $request->getContentType();
$base_url = $request->getBaseUrl();
$data = [
"real_method"=>$real_method,
"scheme_host"=>$scheme_host,
"request_uri"=>$request_uri,
"client_ip"=>$client_ip,
"content_type"=>$content_type,
"base_url"=>$base_url,
];
Log::info("Http Request : ".json_encode($data));
if (strrpos($request_uri,'?')) {
$temp = explode('?', $request_uri);
$temp = explode('/', current($temp));
}else{
$temp = explode('/', $request_uri);
}
$this->controller = $temp[1];
$this->method = $temp[2];
try {
if (strtolower($this->controller)=="api") {
$this->api_version = $temp[2];
$this->controller = $temp[3];
$this->method = $temp[4];
}
$tag = ['@method','@api'];
$request_method = $this->getProperty(
'App\Http\Controllers\\'.ucwords($this->controller).'Controller',
$this->method,
$tag
);
Log::info("request_method: ",$request_method);
if ($request_method['@method'] != strtolower($real_method)) {
throw new \Exception('方法不支援');
}
if (!empty($request_method['@api'])) {
if (empty($this->api_version)) {
throw new \Exception("@api 版本為空");
}
$api = (float) explode(':', $request_method['@api'])[1];
$req_api = (float) explode('v',$this->api_version)[1];
if ($api != $req_api) {
throw new \Exception("@api 版本不正確");
}
}
} catch (\Exception $e) {
throw $e;
}
return $next($request);
}
/**
* Get Class Property Function
* @param $class
* @param $method
* @param $tags
* @return array|null
* @throws \Exception
*/
public function getProperty($class, $method, $tags)
{
try {
$req_property = null;
if (empty($tags)) {
throw new \Exception('Tag 不能空');
}
$controller = new \ReflectionClass($class);
$method = $controller->getMethod($method);
$doc = $method->getDocComment();
$matches = array();
$req_property = array();
foreach ($tags as $item) {
preg_match("/".$item."(.*)(\\r\\n|\\r|\\n)/U", $doc, $matches);
if (isset($matches[1])) {
$req_property[$item] =trim($matches[1]) ;
}
}
if (!isset($req_property['@method'])) {
throw new \Exception('請設定@method 屬性[必填]');
}
return $req_property;
} catch (\Exception $e) {
throw $e;
}
}
}
HttpDiscernMiddleware的任務是完成使用者請求和Controller中的方法進行匹配,透過反射獲取Controller中Function註釋資訊(@method,@api),這樣可以不需要在routes資料夾下配置很多路由資訊了;
在Kernel.php,$routeMiddleware中配置如下:
'discern'=> \App\Http\Middleware\HttpDiscernMiddleware::class,
在RouteServiceProvider 中加入:
public function boot()
{
//
parent::boot();
Route::bind('controller', function ($controller) {
try {
return app($this->namespace.'\\'.ucwords($controller).'Controller');
} catch (\Exception $e) {
throw new \Exception('Controller 解析失敗');
}
});
}
繫結$controller變數為解析後的Controller物件 ;
然後在routes/web.php中 加入:
Route::group([
'middleware'=>[
'discern'
]
],function () {
Route::match(['get','post','put','delete','patch'],'/{controller}/{method}',function ($controller,$method){
try {
$result = $controller->$method();
if(is_scalar($result)){
return response()->json($result);
}
return $result;
} catch (\Exception $e) {
throw new Exception($e->getMessage());
}
});
});
在routes/api.php中加入:
Route::group([
'middleware'=>[
'discern'
]
],function () {
Route::match(['get','post','put','delete','patch'],'/{version}/{controller}/{method}',function ($version,$controller,$method){
try {
$result = $controller->$method();
if(is_scalar($result)){
return response()->json($result);
}
return $result;
} catch (\Exception $e) {
throw new Exception($e->getMessage());
}
});
});
這樣就配置好web.php 和 api.php了。
這些配置好後就只需要在控制器中配置請求method或者api版本資訊 就可以了,不需要寫繁瑣的路由資訊,當然可以根據自己的需要修改。
Demo1: [ 請求uri : http://domain/demo/testGet?user=ethan]
class DemoController extends Controller
{
/**
* Controller-Route Demo
* @method get
* @return array
*/
public function testGet()
{
$user = $this->getParam("user");
return view('home')->with($user);
}
}
@method 定義了請求的方法必須為get
Demo2: [ 請求uri : http://domain/api/v1/demo/testGet?user=eth...]
class DemoController extends Controller
{
/**
* Controller-Route Demo
* @method get
* @api version:1.0
* @return array
*/
public function testGet()
{
$user = $this->getParam("user");
return view('home')->with($user);
}
}
@method 定義了請求的方法必須為get,@api 定義了版本為v1(v是version的簡寫)
其中 @method 支援 [get,post,put,delete]
透過Demo1和Demo2可以透過註釋來控制route路由的請求規則了,達到了簡化路由的目的,並且使用很方便。
本作品採用《CC 協議》,轉載必須註明作者和本文連結