建议先看前几篇文章,不然可能会看不懂,这是我的Laravel专栏
【 Laravel_Attitude_do_it的博客-CSDN博客】
感兴趣的可以去瞅一瞅。
正文开始
继续上篇的内容,我们分析下框架是怎么分配路由的。
我们先定位到dispatchToRouter方法
/**
* Get the route dispatcher callback.
* 获取路由调度器回调
* @return \Closure
*/
protected function dispatchToRouter()
{
return function ($request) {
//绑定request实例到容器
$this->app->instance('request', $request);
//往下面看dispatch方法
return $this->router->dispatch($request);
};
}
/**
* Dispatch the request to the application.
* 将请求分派到应用程序
* @param \Illuminate\Http\Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function dispatch(Request $request)
{
$this->currentRequest = $request;
//往下面看dispatchToRoute方法
return $this->dispatchToRoute($request);
}
/**
* Dispatch the request to a route and return the response.
* 分派请求到路由并且放回一个响应结果
* @param \Illuminate\Http\Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function dispatchToRoute(Request $request)
{
//往下面看runRoute和findRoute方法
return $this->runRoute($request, $this->findRoute($request));
}
/**
* Find the route matching a given request.
* 根据请求找到匹配到的一个路由
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Routing\Route
*/
protected function findRoute($request)
{
// 匹配路由
$this->current = $route = $this->routes->match($request);
//把匹配到的路由绑定到容器
$this->container->instance(Route::class, $route);
return $route;
}
/*
* Find the first route matching a given request.
* 根据请求找到匹配到的一个路由
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Routing\Route
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
public function match(Request $request)
{
//根据请求方法(post,get...)获取路由数组
$routes = $this->get($request->getMethod());
// First, we will see if we can find a matching route for this current request
// method. If we can, great, we can just return it so that it can be called
// by the consumer. Otherwise we will check for routes with another verb.
// 首先,我们将看到如果我们为当前的请求方法可以匹配到路由。如果我们可以,很好,我们可以
// 仅仅返回它那么它可以通过客户消费者被调用。否则,我们将使用另一个动词检查路由
// 往下看matchAgainstRoutes方法
$route = $this->matchAgainstRoutes($routes, $request);
if (! is_null($route)) {
//将路由绑定到给定的执行请求。
return $route->bind($request);
}
// If no route was found we will now check if a matching route is specified by
// another HTTP verb. If it is we will need to throw a MethodNotAllowed and
// inform the user agent of which HTTP verb it should use for this route.
$others = $this->checkForAlternateVerbs($request);
if (count($others) > 0) {
return $this->getRouteForMethods($request, $others);
}
throw new NotFoundHttpException;
}
/**
* Determine if a route in the array matches the request.
* 确定数组中的路由是否与请求匹配
* @param array $routes
* @param \Illuminate\Http\Request $request
* @param bool $includingMethod
* @return \Illuminate\Routing\Route|null
*/
protected function matchAgainstRoutes(array $routes, $request, $includingMethod = true)
{
[$fallbacks, $routes] = collect($routes)->partition(function ($route) {
return $route->isFallback;
});
//放回匹配到的第一个路由,这也是为什么重复路由,后边路由不生效的原因
return $routes->merge($fallbacks)->first(function ($value) use ($request, $includingMethod) {
return $value->matches($request, $includingMethod);
});
}
最后会执行runRoute方法:
/**
* Return the response for the given route.
* 为路由返回一个响应结果
* @param \Illuminate\Http\Request $request 请求实例
* @param \Illuminate\Routing\Route $route 刚刚匹配到的路由实例
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function runRoute(Request $request, Route $route)
{
//设置路由的回调方法
$request->setRouteResolver(function () use ($route) {
return $route;
});
//触发路由绑定的事件
$this->events->dispatch(new RouteMatched($route, $request));
//注意这里会返回Response实例
return $this->prepareResponse($request,
$this->runRouteWithinStack($route, $request)
);
}
/*
* Run the given route within a Stack "onion" instance.
*
* @param \Illuminate\Routing\Route $route
* @param \Illuminate\Http\Request $request
* @return mixed
*/
protected function runRouteWithinStack(Route $route, Request $request)
{
$shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
$this->container->make('middleware.disable') === true;
//获取路由绑定的中间件
$middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);
//这个过程在上篇中有详解此处不再详细描述,只不过这里的then方法调用跟上篇的有区别,
//跟之前一样,then方法的参数同样是一个闭包,但是这个闭包中调用到了Route类的run方法
//在自定义路由时,路由绑定的方法也是通过这个方法被调用的,下边咱们来看一下run方法中
//都做了什么,注意这里会返回Response实例
return (new Pipeline($this->container))
->send($request)
->through($middleware)
->then(function ($request) use ($route) {
//prepareResponse方法最终会调用到Symfony\Component\HttpFoundation\Response类的prepare方法,会返回一个Resoponse实例
return $this->prepareResponse(
$request, $route->run()
);
});
}
/**
* Run the route action and return the response.
* 运行路由绑定的方法并且返回响应结果
* @return mixed
*/
public function run()
{
$this->container = $this->container ?: new Container;
try {
//往下看isControllerAction方法
if ($this->isControllerAction()) {
//如果是控制器方法,则执行这个方法,往下看runController方法
// Route::get('/', 'xxxController@index');或者Route::get('/', [xxxController::class,'index']);这种路由会调用这里
return $this->runController();
}
//如果不是控制器方法,则直接调用闭包,往下看runCallable方法
// Route::get('/', function () {
// return view('welcome');
// });
//这种路由会直接调用function
return $this->runCallable();
} catch (HttpResponseException $e) {
return $e->getResponse();
}
}
/**
* Checks whether the route's action is a controller.
* 检查路由的操作是否为控制器
* @return bool
*/
protected function isControllerAction()
{
// 对$this->action['uses']不理解的小伙伴,可以先看这边文章
// https://blog.csdn.net/Attitude_do_it/article/details/121543993
return is_string($this->action['uses']);
}
/**
* Run the route action and return the response.
*
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
protected function runController()
{
//这里会解析出类和类的方法并执行返回结果,往下看controllerDispatcher,dispatch,getController,getControllerMethod方法
return $this->controllerDispatcher()->dispatch(
$this, $this->getController(), $this->getControllerMethod()
);
}
/**
* Get the dispatcher for the route's controller.
* 这里会返回ControllerDispatcher类的实例
* @return \Illuminate\Routing\Contracts\ControllerDispatcher
*/
public function controllerDispatcher()
{
if ($this->container->bound(ControllerDispatcherContract::class)) {
return $this->container->make(ControllerDispatcherContract::class);
}
return new ControllerDispatcher($this->container);
}
/**
* Get the controller instance for the route.
* 从route实例得到控制器
* @return mixed
*/
public function getController()
{
if (! $this->controller) {
//往下看parseControllerCallback
$class = $this->parseControllerCallback()[0];
$this->controller = $this->container->make(ltrim($class, '\\'));
}
return $this->controller;
}
/**
* Get the controller method used for the route.
* 从route实例中得到控制器方法
* @return string
*/
protected function getControllerMethod()
{
//往下看parseControllerCallback
return $this->parseControllerCallback()[1];
}
/**
* Parse the controller.
* 解析出控制器
* @return array
*/
protected function parseControllerCallback()
{
return Str::parseCallback($this->action['uses']);
}
/**
* Parse a Class[@]method style callback into class and method.
* 解析出控制器和方法
* @param string $callback
* @param string|null $default
* @return array<int, string|null>
*/
public static function parseCallback($callback, $default = null)
{
//如果包含@则分割成数组
return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
}
/**
* 这里调用的是\Illuminate\Routing\Contracts\ControllerDispatcher的dispatch方法
* Dispatch a request to a given controller and method.
* 分派请求到控制器和方法
* @param \Illuminate\Routing\Route $route
* @param mixed $controller
* @param string $method
* @return mixed
*/
public function dispatch(Route $route, $controller, $method)
{
$parameters = $this->resolveClassMethodDependencies(
$route->parametersWithoutNulls(), $controller, $method
);
if (method_exists($controller, 'callAction')) {
return $controller->callAction($method, $parameters);
}
//最想看到的一步来了,调用了路由绑定的控制器和方法
return $controller->{$method}(...array_values($parameters));
}
最终在dispatch方法的最后一行中,我们看到了想要的类方法的执行,到这里请求的处理大多半就算是完成了,下篇我们来讲Laravel是怎么进行请求完的后置操作的