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=ethan]
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路由的请求规则了,达到了简化路由的目的,并且使用很方便。