Laravel 请求类 Request 剖析

Laravel请求原理图

我们已经知道,在 Laravel 框架中,需要通过 Request 对象来获取用户请求信息,该对象引用的完整类名是 Illuminate\Http\Request,而该请求类又继承自 Symfony 的 Symfony\Component\HttpFoundation\Request,所以在介绍 Laravel 的 Request 之前,需要先对 Symfony 底层的 Request 类做个简单的介绍。

Request 底层实现简介

事实上,Laravel 的很多底层组件都是基于 Symfony 的,比如请求、响应、命令行、Cookie、文件等,这里我们主要讨论请求组件。Symfony 也是一个 PHP 框架,并且提供了大量的可复用组件,这些组件可以独立于 Symfony 框架使用,这其中就包括 HttpFoundation 组件,该组件通过 PHP 语言定义了一个针对 HTTP 规范的面向对象抽象层,其中封装了有关 HTTP 请求和响应的一切信息,包括请求、响应、头、Cookie、文件等:

public static function createFromGlobals()
{
    $request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER);

    ...

通过这个组件,我们无需通过类似 $_GET$_POST$_COOKIE$_FILES 这种全局预定义变量来获取请求数据,而是通过面向对象的方式来获取对应的信息,Laravel 在其基础上做了进一步的封装:

$request->input('param');
$request->cookie('cookie');
$request->file('file');

通过这种方式获取请求数据让代码可读性更好,也更加容易测试,因为所有当前用户的请求数据都被封装到一个对象实例中,然后使用可读性良好的方法去访问。

捕获用户请求

捕获用户请求我们在上篇教程中已经讨论了,位于入口文件 public/index.php 中:

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

capture 方法会调用 Symfony Request 对象的底层方法进行一些初始化设置,并将请求数据赋值给 Request 对象实例:

public static function capture()
{
    static::enableHttpMethodParameterOverride();
    return static::createFromBase(SymfonyRequest::createFromGlobals());
}

然后 Laravel 框架会将这个请求对象实例绑定到服务容器中,这样,我们就可以在应用中通过解析该实例获取用户请求数据了。

获取请求对象

前面我们已经说到在请求初始化完成后,Laravel 框架会将请求实例绑定到服务容器,要访问请求对象,可以通过多种方式。

首先,我们可以通过依赖注入的方式在控制器方法中显示声明请求对象:

class RequestController extends Controller
{
    public function fileUpload(Request $request)
    {
        ...
    }

此外,还可以通过全局辅助函数 request() 快速获取当前用户请求实例:

$request = request();
$name = $request->input('name');

最后,我们还可以直接通过解析服务容器的方式获取请求实例,上面两种方式最终也是通过这种方式从服务容器中获取请求实例的:

$request = app('request');   
$request = app(\Illuminate\Http\Request::class);

注:在 Illuminate\Foundation\ApplicationregisterCoreContainerAliases 方法将 Illuminate\Http\Request::class 的别名设置为 request,所以上面两种解析方式获取到的是同一个请求实例,即应用启动初始化时设置的那个请求实例。

访问用户当前请求数据

拿到请求实例后,就可以获取用户请求数据了,关于这方面的内容我们在前面已经介绍过了,参考下面这几篇教程即可:

处了用户请求数据之外,还可以获取其它隐式信息,比如请求方式、请求 URL、请求头、用户IP、是否使用 HTTPS、是否通过 Pjax 加载页面等:

  • method():返回当前请求方式,如 GET/POST/PUT/DELETE 等;
  • path():返回当前请求路径(不包含域名),如访问 https://xueyuanjun.com/post/9804.html 返回 post/9804.html
  • url():返回完整请求URL(包含域名);
  • is():通过传入参数作为模式字符串与当前请求URL进行模式匹配,看请求URL中是否包含给定模式字符串;
  • ip():返回用户 IP 地址;
  • header():通过传入参数获取对应的请求头信息,为空则返回所有请求头;
  • server():获取 $_SERVER 中的数据,用法和 header() 方法一样;
  • secure():是否通过 HTTPS 访问;
  • pjax():是否通过 Pjax 访问;
  • bearerToken():从请求头中获取 Bearer Token 信息。

当然,除此之外,Request 请求类还提供了很多其他方法,你可以通过查看 Illuminate\Http\Request 源码进行了解。

访问持久化请求数据

注:所谓持久化的用户请求数据通常指的是用户上次请求输入的数据及用户 Cookie 数据。

除此之外,Laravel 的 Request 对象还支持与 Session 和 Cookie 进行交互,获取持久化的用户请求数据,关于用户请求数据的持久化我们会在下一篇用户响应中介绍,这里我们简单介绍下如何访问持久化的用户请求数据。

要访问用户上次请求输入的数据,可以通过 old() 方法:

// 不传参数返回所有保存的上次请求数据
$old_input = $request->old('old_input');

还可以通过 flash() 方法刷新此次请求数据以便在下次请求时使用,以及 flush() 方法清除所有持久化的用户请求数据。更多方法请参考 InteractsWithFlashData Trait,这里就不一一罗列了。

关于上次请求数据持久化的功能底层是通过维护 Session 数据来实现的,我们在下一篇介绍 Laravel 响应的时候将详细阐述其实现原理。

上一篇: Laravel 底层是如何处理 HTTP 请求的

下一篇: Laravel 响应类 Response 剖析