Skip to content
全部文档

HTTP 请求

简介

Laravel 的 Illuminate\Http\Request 类提供了面向对象的方式,用于与应用正在处理的当前 HTTP 请求交互,以及检索随请求提交的输入、Cookie 和文件。

与请求交互

访问请求

要通过依赖注入获取当前 HTTP 请求的实例,应在路由闭包或控制器方法上类型提示 Illuminate\Http\Request 类。传入的请求实例会由 Laravel 服务容器 自动注入:

php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * Store a new user.
     */
    public function store(Request $request): RedirectResponse
    {
        $name = $request->input('name');

        // Store the user...

        return redirect('/users');
    }
}

如前所述,你也可以在路由闭包上类型提示 Illuminate\Http\Request 类。闭包执行时,服务容器会自动将传入请求注入其中:

php
use Illuminate\Http\Request;

Route::get('/', function (Request $request) {
    // ...
});

依赖注入与路由参数

若控制器方法还期望从路由参数获取输入,应将路由参数列在其他依赖之后。例如,若路由定义如下:

php
use App\Http\Controllers\UserController;

Route::put('/user/{id}', [UserController::class, 'update']);

你仍可以类型提示 Illuminate\Http\Request,并通过如下定义控制器方法来访问 id 路由参数:

php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * Update the specified user.
     */
    public function update(Request $request, string $id): RedirectResponse
    {
        // Update the user...

        return redirect('/users');
    }
}

请求路径、主机与方法

Illuminate\Http\Request 实例提供了多种检查传入 HTTP 请求的方法,并扩展了 Symfony\Component\HttpFoundation\Request 类。下面我们将讨论其中几个最重要的方法。

获取请求路径

path 方法返回请求的路径信息。因此,若传入请求的目标是 http://example.com/foo/barpath 方法将返回 foo/bar

php
$uri = $request->path();

检查请求路径 / 路由

is 方法允许你验证传入请求路径是否匹配给定模式。使用该方法时可用 * 字符作为通配符:

php
if ($request->is('admin/*')) {
    // ...
}

使用 routeIs 方法,你可以判断传入请求是否匹配了某个 命名路由

php
if ($request->routeIs('admin.*')) {
    // ...
}

获取请求 URL

要获取传入请求的完整 URL,可以使用 urlfullUrl 方法。url 方法返回不含查询字符串的 URL,而 fullUrl 方法包含查询字符串:

php
$url = $request->url();

$urlWithQueryString = $request->fullUrl();

若希望向当前 URL 追加查询字符串数据,可以调用 fullUrlWithQuery 方法。该方法会将给定的查询字符串变量数组与当前查询字符串合并:

php
$request->fullUrlWithQuery(['type' => 'phone']);

若希望获取不含某个给定查询字符串参数的当前 URL,可以使用 fullUrlWithoutQuery 方法:

php
$request->fullUrlWithoutQuery(['type']);

获取请求主机

你可以通过 hosthttpHostschemeAndHttpHost 方法获取传入请求的「主机」:

php
// http://localhost:8000
$request->host(); // localhost
$request->httpHost(); // localhost:8000
$request->schemeAndHttpHost(); // http://localhost:8000

获取请求方法

method 方法会返回请求的 HTTP 动词。你可以使用 isMethod 方法验证 HTTP 动词是否匹配给定字符串:

php
$method = $request->method();

if ($request->isMethod('post')) {
    // ...
}

请求头

你可以使用 header 方法从 Illuminate\Http\Request 实例检索请求头。若请求上不存在该头,将返回 null。不过,header 方法接受可选的第二个参数,在请求上不存在该头时返回该值:

php
$value = $request->header('X-Header-Name');

$value = $request->header('X-Header-Name', 'default');

hasHeader 方法可用于判断请求是否包含给定头:

php
if ($request->hasHeader('X-Header-Name')) {
    // ...
}

为方便起见,可以使用 bearerToken 方法从 Authorization 头检索 bearer 令牌。若不存在该头,将返回空字符串:

php
$token = $request->bearerToken();

请求 IP 地址

ip 方法可用于检索向你的应用发起请求的客户端 IP 地址:

php
$ipAddress = $request->ip();

若希望检索 IP 地址数组(包括由代理转发的全部客户端 IP 地址),可以使用 ips 方法。「原始」客户端 IP 地址会位于数组末尾:

php
$ipAddresses = $request->ips();

一般而言,IP 地址应视为不可信的、由用户控制的输入,仅用于信息性目的。

内容协商

Laravel 提供了多种方法,通过 Accept 头检查传入请求所请求的内容类型。首先,getAcceptableContentTypes 方法会返回包含请求接受的全部内容类型的数组:

php
$contentTypes = $request->getAcceptableContentTypes();

accepts 方法接受内容类型数组,若请求接受其中任一内容类型则返回 true,否则返回 false

php
if ($request->accepts(['text/html', 'application/json'])) {
    // ...
}

你可以使用 prefers 方法,判断给定内容类型数组中请求最偏好哪一种。若请求不接受所提供的任何内容类型,将返回 null

php
$preferred = $request->prefers(['text/html', 'application/json']);

由于许多应用只提供 HTML 或 JSON,你可以使用 expectsJson 方法快速判断传入请求是否期望 JSON 响应:

php
if ($request->expectsJson()) {
    // ...
}

若需要判断请求是否特别偏好 Markdown,或是否在其他内容类型中也接受 Markdown(例如为消费 Markdown 响应的 AI agent 或其他客户端提供服务时),可以使用 wantsMarkdownacceptsMarkdown 方法:

php
if ($request->wantsMarkdown()) {
    // The client's most preferred content type is text/markdown...
}

if ($request->acceptsMarkdown()) {
    // The client accepts Markdown responses...
}

PSR-7 请求

PSR-7 标准 规定了 HTTP 消息(包括请求和响应)的接口。若希望获取 PSR-7 请求实例而非 Laravel 请求,需要先安装一些库。Laravel 使用 Symfony HTTP Message Bridge 组件,将典型的 Laravel 请求和响应转换为 PSR-7 兼容实现:

shell
composer require symfony/psr-http-message-bridge
composer require nyholm/psr7

安装这些库后,你可以在路由闭包或控制器方法上类型提示请求接口,以获取 PSR-7 请求:

php
use Psr\Http\Message\ServerRequestInterface;

Route::get('/', function (ServerRequestInterface $request) {
    // ...
});

INFO

若你从路由或控制器返回 PSR-7 响应实例,它会自动转换回 Laravel 响应实例,并由框架显示。

输入

检索输入

检索全部输入数据

你可以使用 all 方法将传入请求的全部输入数据作为 array 检索。无论传入请求来自 HTML 表单还是 XHR 请求,都可以使用该方法:

php
$input = $request->all();

使用 collect 方法,你可以将传入请求的全部输入数据作为 集合 检索:

php
$input = $request->collect();

collect 方法也允许你将传入请求输入的子集作为集合检索:

php
$request->collect('users')->each(function (string $user) {
    // ...
});

检索输入值

使用几个简单方法,你可以从 Illuminate\Http\Request 实例访问全部用户输入,而无需关心请求使用了哪个 HTTP 动词。无论 HTTP 动词如何,都可以使用 input 方法检索用户输入:

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

你可以将默认值作为第二个参数传给 input 方法。若请求上不存在所请求的输入值,将返回该默认值:

php
$name = $request->input('name', 'Sally');

处理包含数组输入的表单时,使用「点」语法访问数组:

php
$name = $request->input('products.0.name');

$names = $request->input('products.*.name');

你可以不带任何参数调用 input 方法,以将全部输入值作为关联数组检索:

php
$input = $request->input();

从查询字符串检索输入

input 方法从整个请求负载(包括查询字符串)检索值,而 query 方法仅从查询字符串检索值:

php
$name = $request->query('name');

若所请求的查询字符串值数据不存在,将返回该方法的第二个参数:

php
$name = $request->query('name', 'Helen');

你可以不带任何参数调用 query 方法,以将全部查询字符串值作为关联数组检索:

php
$query = $request->query();

检索 JSON 输入值

向应用发送 JSON 请求时,只要请求的 Content-Type 头正确设置为 application/json,即可通过 input 方法访问 JSON 数据。你甚至可以使用「点」语法检索嵌套在 JSON 数组 / 对象中的值:

php
$name = $request->input('user.name');

检索 Stringable 输入值

你可以使用 string 方法将请求数据作为 Illuminate\Support\Stringable 实例检索,而不是作为原始 string

php
$name = $request->string('name')->trim();

检索整数输入值

要将输入值作为整数检索,可以使用 integer 方法。该方法会尝试将输入值转换为整数。若输入不存在或转换失败,将返回你指定的默认值。这对分页或其他数值输入特别有用:

php
$perPage = $request->integer('per_page');

检索布尔输入值

处理复选框等 HTML 元素时,应用可能收到实际上是字符串的「真值」,例如「true」或「on」。为方便起见,你可以使用 boolean 方法将这些值作为布尔值检索。boolean 方法对 1、「1」、true、「true」、「on」和「yes」返回 true。所有其他值将返回 false

php
$archived = $request->boolean('archived');

检索数组输入值

包含数组的输入值可以使用 array 方法检索。该方法始终会将输入值转换为数组。若请求不包含给定名称的输入值,将返回空数组:

php
$versions = $request->array('versions');

检索日期输入值

为方便起见,包含日期 / 时间的输入值可以使用 date 方法作为 Carbon 实例检索。若请求不包含给定名称的输入值,将返回 null

php
$birthday = $request->date('birthday');

date 方法接受的第二和第三个参数可分别用于指定日期的格式和时区:

php
$elapsed = $request->date('elapsed', '!H:i', 'Europe/Madrid');

若输入值存在但格式无效,将抛出 InvalidArgumentException;因此建议在调用 date 方法之前先验证输入。

检索区间输入值

包含时长的输入值可以使用 interval 方法作为 CarbonInterval 实例检索。若请求不包含给定名称的输入值,将返回 null

php
$duration = $request->interval('duration');

若输入值是数字,可以提供单位作为第二个参数。单位可以是 secondminuteday 等字符串,也可以是 Carbon\Unit 枚举实例:

php
use Carbon\Unit;

$timeout = $request->interval('timeout', 'second');

$delay = $request->interval('delay', Unit::Minute);

若输入值存在但格式无效,将抛出 InvalidArgumentException;因此建议在调用 interval 方法之前先验证输入。

检索枚举输入值

对应 PHP 枚举 的输入值也可以从请求中检索。若请求不包含给定名称的输入值,或枚举没有与输入值匹配的底层值,将返回 nullenum 方法的第一和第二个参数分别接受输入值名称和枚举类:

php
use App\Enums\Status;

$status = $request->enum('status', Status::class);

你也可以提供一个在值缺失或无效时返回的默认值:

php
$status = $request->enum('status', Status::class, Status::Pending);

若输入值是对应 PHP 枚举的值数组,可以使用 enums 方法将这些值作为枚举实例数组检索:

php
use App\Enums\Product;

$products = $request->enums('products', Product::class);

通过动态属性检索输入

你也可以使用 Illuminate\Http\Request 实例上的动态属性访问用户输入。例如,若应用的某个表单包含 name 字段,可以像这样访问该字段的值:

php
$name = $request->name;

使用动态属性时,Laravel 会先在请求负载中查找该参数的值。若不存在,Laravel 会在匹配路由的参数中搜索该字段。

检索部分输入数据

若需要检索输入数据的子集,可以使用 onlyexcept 方法。这两种方法都接受单个 array 或动态参数列表:

php
$input = $request->only(['username', 'password']);

$input = $request->only('username', 'password');

$input = $request->except(['credit_card']);

$input = $request->except('credit_card');

WARNING

only 方法会返回你请求的全部键 / 值对;但不会返回请求上不存在的键 / 值对。

输入是否存在

你可以使用 has 方法判断请求上是否存在某个值。若该值存在于请求上,has 方法返回 true

php
if ($request->has('name')) {
    // ...
}

传入数组时,has 方法会判断所有指定值是否都存在:

php
if ($request->has(['name', 'email'])) {
    // ...
}

若任一指定值存在,hasAny 方法返回 true

php
if ($request->hasAny(['name', 'email'])) {
    // ...
}

若请求上存在某个值,whenHas 方法会执行给定闭包:

php
$request->whenHas('name', function (string $input) {
    // ...
});

可以向 whenHas 方法传入第二个闭包,在指定值不存在于请求上时执行:

php
$request->whenHas('name', function (string $input) {
    // The "name" value is present...
}, function () {
    // The "name" value is not present...
});

若希望判断某个值是否存在于请求上且不是空字符串,可以使用 filled 方法:

php
if ($request->filled('name')) {
    // ...
}

若希望判断某个值是否缺失于请求或为空字符串,可以使用 isNotFilled 方法:

php
if ($request->isNotFilled('name')) {
    // ...
}

传入数组时,isNotFilled 方法会判断所有指定值是否都缺失或为空:

php
if ($request->isNotFilled(['name', 'email'])) {
    // ...
}

若任一指定值不是空字符串,anyFilled 方法返回 true

php
if ($request->anyFilled(['name', 'email'])) {
    // ...
}

若某个值存在于请求上且不是空字符串,whenFilled 方法会执行给定闭包:

php
$request->whenFilled('name', function (string $input) {
    // ...
});

可以向 whenFilled 方法传入第二个闭包,在指定值未「填充」时执行:

php
$request->whenFilled('name', function (string $input) {
    // The "name" value is filled...
}, function () {
    // The "name" value is not filled...
});

要判断给定键是否不在请求中,可以使用 missingwhenMissing 方法:

php
if ($request->missing('name')) {
    // ...
}

$request->whenMissing('name', function () {
    // The "name" value is missing...
}, function () {
    // The "name" value is present...
});

合并额外输入

有时你可能需要手动将额外输入合并到请求的现有输入数据中。为此,可以使用 merge 方法。若给定输入键已存在于请求上,将被传给 merge 方法的数据覆盖:

php
$request->merge(['votes' => 0]);

若对应键尚未存在于请求的输入数据中,可以使用 mergeIfMissing 方法将输入合并到请求中:

php
$request->mergeIfMissing(['votes' => 0]);

旧输入

Laravel 允许你在下一次请求期间保留某次请求的输入。该功能在检测到验证错误后重新填充表单时特别有用。不过,若你使用 Laravel 内置的 验证功能,可能无需直接手动使用这些会话输入闪存方法,因为 Laravel 的部分内置验证机制会自动调用它们。

将输入闪存到会话

Illuminate\Http\Request 类上的 flash 方法会将当前输入闪存到 会话,以便在用户对应用的下一次请求期间可用:

php
$request->flash();

你也可以使用 flashOnlyflashExcept 方法将请求数据的子集闪存到会话。这些方法有助于将密码等敏感信息排除在会话之外:

php
$request->flashOnly(['username', 'email']);

$request->flashExcept('password');

闪存输入后重定向

由于你经常希望将输入闪存到会话,然后重定向到上一页,可以使用 withInput 方法轻松地将输入闪存链式接到重定向上:

php
return redirect('/form')->withInput();

return redirect()->route('user.create')->withInput();

return redirect('/form')->withInput(
    $request->except('password')
);

检索旧输入

要从上一次请求检索闪存的输入,请在 Illuminate\Http\Request 实例上调用 old 方法。old 方法会从 会话 中取出先前闪存的输入数据:

php
$username = $request->old('username');

Laravel 还提供了全局 old 辅助函数。若你在 Blade 模板 中显示旧输入,使用 old 辅助函数重新填充表单会更方便。若给定字段没有旧输入,将返回 null

blade
<input type="text" name="username" value="{{ old('username') }}">

Cookies

从请求检索 Cookie

Laravel 框架创建的所有 Cookie 都经过加密,并用认证码签名,意味着若被客户端更改将被视为无效。要从请求检索 Cookie 值,请在 Illuminate\Http\Request 实例上使用 cookie 方法:

php
$value = $request->cookie('name');

输入修剪与规范化

默认情况下,Laravel 会在应用的全局中间件栈中包含 Illuminate\Foundation\Http\Middleware\TrimStringsIlluminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull 中间件。这些中间件会自动修剪请求上所有传入的字符串字段,并将任何空字符串字段转换为 null。这样你就不必在路由和控制器中担心这些规范化问题。

禁用输入规范化

若希望对所有请求禁用该行为,可以在应用的 bootstrap/app.php 文件中调用 $middleware->remove 方法,从应用的中间件栈中移除这两个中间件:

php
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
use Illuminate\Foundation\Http\Middleware\TrimStrings;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->remove([
        ConvertEmptyStringsToNull::class,
        TrimStrings::class,
    ]);
})

若希望对发往应用的部分请求禁用字符串修剪和空字符串转换,可以在应用的 bootstrap/app.php 文件中使用 trimStringsconvertEmptyStringsToNull 中间件方法。这两种方法都接受闭包数组,闭包应返回 truefalse,以指示是否应跳过输入规范化:

php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->convertEmptyStringsToNull(except: [
        fn (Request $request) => $request->is('admin/*'),
    ]);

    $middleware->trimStrings(except: [
        fn (Request $request) => $request->is('admin/*'),
    ]);
})

文件

检索上传的文件

你可以使用 file 方法或动态属性从 Illuminate\Http\Request 实例检索上传的文件。file 方法返回 Illuminate\Http\UploadedFile 类的实例,该类扩展了 PHP 的 SplFileInfo 类,并提供了多种与文件交互的方法:

php
$file = $request->file('photo');

$file = $request->photo;

你可以使用 hasFile 方法判断请求上是否存在文件:

php
if ($request->hasFile('photo')) {
    // ...
}

若上传的文件是需要在存储前处理的图像,可以使用 image 方法检索 Illuminate\Image\Image 实例;若文件不存在则返回 null

php
$image = $request->image('photo');

有关处理图像的更多信息,请参阅完整的 图像处理文档

验证上传是否成功

除了检查文件是否存在外,你还可以通过 isValid 方法验证上传文件时是否没有问题:

php
if ($request->file('photo')->isValid()) {
    // ...
}

文件路径与扩展名

UploadedFile 类还包含访问文件完整路径及其扩展名的方法。extension 方法会尝试根据文件内容猜测扩展名。该扩展名可能与客户端提供的扩展名不同:

php
$path = $request->photo->path();

$extension = $request->photo->extension();

其他文件方法

UploadedFile 实例上还有多种其他可用方法。有关这些方法的更多信息,请查看该类的 API 文档

存储上传的文件

要存储上传的文件,通常会使用已配置的某个 文件系统UploadedFile 类有一个 store 方法,会将上传的文件移动到你的某个磁盘,该磁盘可以是本地文件系统上的位置,也可以是 Amazon S3 等云存储位置。

store 方法接受相对于文件系统已配置根目录的文件存储路径。该路径不应包含文件名,因为会自动生成唯一 ID 作为文件名。

store 方法还接受可选的第二个参数,即用于存储文件的磁盘名称。该方法会返回相对于磁盘根目录的文件路径:

php
$path = $request->photo->store('images');

$path = $request->photo->store('images', 's3');

若不想自动生成文件名,可以使用 storeAs 方法,它接受路径、文件名和磁盘名作为参数:

php
$path = $request->photo->storeAs('images', 'filename.jpg');

$path = $request->photo->storeAs('images', 'filename.jpg', 's3');

INFO

有关 Laravel 中文件存储的更多信息,请参阅完整的 文件存储文档

配置受信任的代理

在终止 TLS / SSL 证书的负载均衡器后面运行应用时,你可能会注意到使用 url 辅助函数时应用有时不会生成 HTTPS 链接。通常这是因为应用从负载均衡器的 80 端口接收转发流量,并不知道应生成安全链接。

要解决此问题,可以启用 Laravel 应用中包含的 Illuminate\Http\Middleware\TrustProxies 中间件,它允许你快速自定义应用应信任的负载均衡器或代理。应在应用的 bootstrap/app.php 文件中使用 trustProxies 中间件方法指定受信任的代理:

php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->trustProxies(at: [
        '192.168.1.1',
        '10.0.0.0/8',
    ]);
})

除了配置受信任的代理外,你还可以配置应信任的代理头:

php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->trustProxies(headers: Request::HEADER_X_FORWARDED_FOR |
        Request::HEADER_X_FORWARDED_HOST |
        Request::HEADER_X_FORWARDED_PORT |
        Request::HEADER_X_FORWARDED_PROTO |
        Request::HEADER_X_FORWARDED_AWS_ELB
    );
})

INFO

若你使用 AWS Elastic Load Balancing,headers 值应为 Request::HEADER_X_FORWARDED_AWS_ELB。若负载均衡器使用 RFC 7239 中的标准 Forwarded 头,headers 值应为 Request::HEADER_FORWARDED。有关可在 headers 值中使用的常量的更多信息,请参阅 Symfony 关于 信任代理 的文档。

信任所有代理

若你使用 Amazon AWS 或其他「云」负载均衡提供商,可能不知道实际均衡器的 IP 地址。在这种情况下,你可以使用 * 信任所有代理:

php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->trustProxies(at: '*');
})

配置受信任的主机

默认情况下,无论 HTTP 请求的 Host 头内容如何,Laravel 都会响应它收到的所有请求。此外,在 Web 请求期间生成指向应用的绝对 URL 时,会使用 Host 头的值。

通常,你应配置 Nginx 或 Apache 等 Web 服务器,使其仅向应用发送匹配给定主机名的请求。不过,若你无法直接自定义 Web 服务器,且需要指示 Laravel 仅响应某些主机名,可以通过为应用启用 Illuminate\Http\Middleware\TrustHosts 中间件来实现。

要启用 TrustHosts 中间件,应在应用的 bootstrap/app.php 文件中调用 trustHosts 中间件方法。使用该方法的 at 参数,你可以指定应用应响应的主机名。主机名字符串会被视为正则表达式。带有其他 Host 头的传入请求将被拒绝:

php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->trustHosts(at: ['^laravel\.test$']);
})

默认情况下,来自应用 URL 子域名的请求也会被自动信任。若希望禁用该行为,可以使用 subdomains 参数:

php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->trustHosts(at: ['^laravel\.test$'], subdomains: false);
})

若需要访问应用的配置文件或数据库来确定受信任的主机,可以向 at 参数提供一个闭包:

php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->trustHosts(at: fn () => config('app.trusted_hosts'));
})