老王,你给我说说 Laravel 的请求是怎么到达控制器的

本文深入剖析了Laravel框架中一个请求从入口到控制器的全过程,包括入口文件、Kernel对象处理、中间件调用、路由匹配及调度等关键步骤。

老王啊,你总在说Laravel的东西,能不能给我大概简单的说一下一个请求是怎么样到达我写在控制的代码中去的。中间都经历了哪些东西啊。

入口

Laravel5.8 入口文件为public/index.php


$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);


$response = $kernel->handle(

    $request = Illuminate\Http\Request::capture()

);


$response->send();


$kernel->terminate($request, $response);

创建了一个Kernel对象,调用handler处理请求,获取返回结果。将返回结果输出到客户端,处理terminate操作。

Kernel中如何处理请求

容器里绑定的是App\Http\Kernel,继承于Illuminate\Foundation\Http\Kernel


    public function handle($request)

    {

        try {

            $request->enableHttpMethodParameterOverride();


            $response = $this->sendRequestThroughRouter($request);

        } catch (Exception $e) {

            $this->reportException($e);


            $response = $this->renderException($request, $e);

        } catch (Throwable $e) {

            $this->reportException($e = new FatalThrowableError($e));


            $response = $this->renderException($request, $e);

        }


        $this->app['events']->dispatch(

            new Events\RequestHandled($request, $response)

        );


        return $response;

    }

Kernel中调用sendRequestThroughRouter方法,将请求传递到路由处理当中。


protected function sendRequestThroughRouter($request)

{

    $this->app->instance('request', $request);


    Facade::clearResolvedInstance('request');


    $this->bootstrap();


    return (new Pipeline($this->app))

                ->send($request)

                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)

                ->then($this->dispatchToRouter());

}

sendRequestThroughRouter当中,在app中绑定了request实例,并解绑掉其他request实例对象。这样在程序其他地方都能通过app()->make('request')获取到request实例对象。

调用bootstrap方法,加载引导类。

创建一个Pipeline对象,将路由调度与中间件放入调用链当中。所有request先经过全局的中间件,然后在通过路由分发。


    protected function dispatchToRouter()

    {

        return function ($request) {

            $this->app->instance('request', $request);


            return $this->router->dispatch($request);

        };

    }


因为Piepline调用链都是一个个的回调方法,所以在dispatchToRouter返回了一个匿名回调函数。使用Kernelroute属性进行调度。

Kernelroute是一个Illuminate\Routing\Router对象。

路由调度


//Illuminate\Routing\Router

public function dispatch(Request $request)

{

    $this->currentRequest = $request;


    return $this->dispatchToRoute($request);

}

public function dispatchToRoute(Request $request)

{

    return $this->runRoute($request, $this->findRoute($request));

}

protected function runRoute(Request $request, Route $route)

{

    $request->setRouteResolver(function () use ($route) {

        return $route;

    });

    $this->events->dispatch(new Events\RouteMatched($route, $request));

    return $this->prepareResponse($request,

        $this->runRouteWithinStack($route, $request)

    );

}

从上面的方法可以看出,最终通过findRoute查找当前匹配的路由对象,并调用runRoute处理请求返回结果。

怎么找到路由的


//Illuminate\Routing\Router

protected function findRoute($request)

{

    $this->current = $route = $this->routes->match($request);

    $this->container->instance(Route::class, $route);

    return $route;

}

对路由的匹配,是通过routes这个路由Collections去匹配的。


//Illuminate\Routing\RouteCollection

    public function match(Request $request)

    {

        $routes = $this->get($request->getMethod());

        $route = $this->matchAgainstRoutes($routes, $request);

        if (! is_null($route)) {

            return $route->bind($request);

        }

        $others = $this->checkForAlternateVerbs($request);

        if (count($others) > 0) {

            return $this->getRouteForMethods($request, $others);

        }

        throw new NotFoundHttpException;

    }

    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);

        });

    }


先通过请求的方法获取当前方法下可用的路由集合,在从这些集合中去遍历获取第一个匹配的路由。集合中每个item是一个Illuminate\Routing\Router对象。因此最终判断路由与请求是否匹配调用的是Illuminate\Routing\Router中的matches方法。


//Illuminate\Routing\Router

public function matches(Request $request, $includingMethod = true)

{

    $this->compileRoute();

    foreach ($this->getValidators() as $validator) {

        if (! $includingMethod && $validator instanceof MethodValidator) {

            continue;

        }

        if (! $validator->matches($this, $request)) {

            return false;

        }

    }

    return true;

}

public static function getValidators()

{

    if (isset(static::$validators)) {

        return static::$validators;

    }

    return static::$validators = [

        new UriValidator, new MethodValidator,

        new SchemeValidator, new HostValidator,

    ];

}

Illuminate\Routing\Router提供了四个默认的验证器,当四个验证器通过的时候才会匹配成功。四个验证器分别是UriValidator验证访问路径,MethodValidator验证请求方法,SchemeValidator验证访问协议,HostValidator验证域名。其中对uri的验证内部是使用正则表达式验证。

路由调度怎么处理请求


//Illuminate\Routing\Router

protected function runRouteWithinStack(Route $route, Request $request)

{

    $shouldSkipMiddleware = $this->container->bound('middleware.disable') &&

                            $this->container->make('middleware.disable') === true;


    $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);


    return (new Pipeline($this->container))

                    ->send($request)

                    ->through($middleware)

                    ->then(function ($request) use ($route) {

                        return $this->prepareResponse(

                            $request, $route->run()

                        );

                    });

}

public function run()

{

    $this->container = $this->container ?: new Container;

    try {

        if ($this->isControllerAction()) {

            return $this->runController();

        }

        return $this->runCallable();

    } catch (HttpResponseException $e) {

        return $e->getResponse();

    }

}

路由对请求的处理也是返回一个Pipeline,先将请求通过中间件,然后在执行路由的run方法。在run方法里面判断当前是执行控制器方法还是回调方法,根据不同类型分开执行。

怎么执行


protected function isControllerAction()

{

    return is_string($this->action['uses']);

}

protected function runCallable()

{

    $callable = $this->action['uses'];


    return $callable(...array_values($this->resolveMethodDependencies(

        $this->parametersWithoutNulls(), new ReflectionFunction($this->action['uses'])

    )));

}

protected function runController()

{

    return $this->controllerDispatcher()->dispatch(

        $this, $this->getController(), $this->getControllerMethod()

    );

}

通过当前路由的action配置判断是否是控制器或者回调方法。从代码中可以看到,其实就是我们路由配置中的第二个参数对应到action['user']。当我们第二参数是一个字符串的时候则认为是控制器方法,将请求转发到控制器里去处理。否则执行回调函数处理。

到这里,我们的请求就真的到达了我们的控制器的方法中,开始执行我们写的代码了。

### 老王与控制算法在IT领域的应用 在探讨老王所涉及的控制算法及其在IT领域的应用时,可以发现这些算法不仅限于理论研究,在实际项目中也扮演着重要角色。例如,在自动驾驶汽车的研发过程中,路径规划是一个核心环节,A*算法作为一种启发式的搜索方法被广泛应用于此类场景中[^2]。 对于像老王这样的专家而言,除了讲解基础概念外,更注重如何将控制算法融入具体应用场景之中。特别是在生物和医学领域,通过深度学习技术实现数据处理、疾病预测等功能成为新的发展方向之一[^3]。这意味着控制算法不再仅仅局限于传统意义上的工业自动化或者机器人运动控制等方面;随着跨学科融合趋势日益明显,其潜在价值正逐渐显现出来。 此外,在企业级应用程序开发方面,尽管AI辅助编程工具能够显著提高初期效率并减少错误率,但在面对复杂业务逻辑以及持续优化的需求时仍存在一定局限性。因此,针对特定行业的定制化解决方案变得尤为重要——这也是为什么越来越多的企业开始重视内部培养既懂专业技术又熟悉行业背景的人才队伍[^4]。 ```cpp // 示例C++代码片段展示了一个简单的PID控制器实现方式 class PIDController { public: double Kp, Ki, Kd; double prev_error = 0.0; double integral = 0.0; PIDController(double kp, double ki, double kd): Kp(kp), Ki(ki), Kd(kd) {} double compute(double setpoint, double pv) { double error = setpoint - pv; integral += error; // 积分项累加误差 double derivative = error - prev_error; // 微分项计算当前时刻变化量 double output = (Kp * error) + (Ki * integral) + (Kd * derivative); prev_error = error; return output; } }; ``` #### 控制算法的实际案例分析 以自动驾驶为例,车辆需要不断地感知周围环境并通过传感器获取实时路况信息。此时,利用诸如卡尔曼滤波器等高级估计技术来提升定位精度就显得尤为关键。同时,为了确保行驶安全性和舒适度,还需要借助纵向速度调节机制(如自适应巡航控制系统ACC),横向转向调整策略(车道保持辅助LKA)等一系列措施共同作用下完成整个驾驶任务。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值