跳转至内容

HTTP 请求

简介

Laravel 的 Illuminate\Http\Request 类提供了一种面向对象的方式来与应用程序当前处理的 HTTP 请求进行交互,并获取随请求提交的输入、Cookie 和文件。

与请求交互

访问请求

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

1<?php
2 
3namespace App\Http\Controllers;
4 
5use Illuminate\Http\RedirectResponse;
6use Illuminate\Http\Request;
7 
8class UserController extends Controller
9{
10 /**
11 * Store a new user.
12 */
13 public function store(Request $request): RedirectResponse
14 {
15 $name = $request->input('name');
16 
17 // Store the user...
18 
19 return redirect('/users');
20 }
21}

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

1use Illuminate\Http\Request;
2 
3Route::get('/', function (Request $request) {
4 // ...
5});

依赖注入与路由参数

如果你的控制器方法还需要从路由参数中获取输入,你应该将路由参数放在其他依赖项之后。例如,如果你的路由定义如下

1use App\Http\Controllers\UserController;
2 
3Route::put('/user/{id}', [UserController::class, 'update']);

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

1<?php
2 
3namespace App\Http\Controllers;
4 
5use Illuminate\Http\RedirectResponse;
6use Illuminate\Http\Request;
7 
8class UserController extends Controller
9{
10 /**
11 * Update the specified user.
12 */
13 public function update(Request $request, string $id): RedirectResponse
14 {
15 // Update the user...
16 
17 return redirect('/users');
18 }
19}

请求路径、主机和方法

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

获取请求路径

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

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

检查请求路径 / 路由

is 方法允许你验证传入的请求路径是否匹配指定的模式。在使用此方法时,你可以使用 * 字符作为通配符。

1if ($request->is('admin/*')) {
2 // ...
3}

使用 routeIs 方法,你可以确定传入的请求是否匹配某个 命名路由

1if ($request->routeIs('admin.*')) {
2 // ...
3}

获取请求 URL

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

1$url = $request->url();
2 
3$urlWithQueryString = $request->fullUrl();

如果你想向当前 URL 追加查询字符串数据,可以调用 fullUrlWithQuery 方法。此方法会将给定的查询字符串变量数组与当前查询字符串合并。

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

如果你想在获取当前 URL 时排除某个给定的查询字符串参数,可以使用 fullUrlWithoutQuery 方法。

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

获取请求主机

你可以通过 hosthttpHostschemeAndHttpHost 方法获取传入请求的“主机”。

1$request->host();
2$request->httpHost();
3$request->schemeAndHttpHost();

获取请求方法

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

1$method = $request->method();
2 
3if ($request->isMethod('post')) {
4 // ...
5}

请求头

你可以使用 header 方法从 Illuminate\Http\Request 实例中获取请求头。如果请求中不存在该头信息,则返回 null。不过,header 方法接受一个可选的第二个参数,如果头信息不存在,将返回该参数的值。

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

hasHeader 方法可用于确定请求是否包含给定的头信息。

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

为方便起见,可以使用 bearerToken 方法从 Authorization 头中获取令牌。如果不存在此类头信息,则返回空字符串。

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

请求 IP 地址

ip 方法可用于获取向你的应用程序发起请求的客户端 IP 地址。

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

如果你想获取 IP 地址数组(包括代理转发的所有客户端 IP 地址),可以使用 ips 方法。“原始”客户端 IP 地址将位于数组的末尾。

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

通常情况下,IP 地址应被视为不可信的、受用户控制的输入,仅供参考。

内容协商

Laravel 提供了几种通过 Accept 头检查传入请求所需内容类型的方法。首先,getAcceptableContentTypes 方法将返回一个包含请求所接受的所有内容类型的数组。

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

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

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

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

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

由于许多应用程序只提供 HTML 或 JSON,你可以使用 expectsJson 方法快速确定传入的请求是否期望 JSON 响应。

1if ($request->expectsJson()) {
2 // ...
3}

如果你需要确定请求是否特别偏好 Markdown,或者在其他内容类型中是否接受 Markdown(例如在服务于 AI 智能体或其他消费 Markdown 响应的客户端时),可以使用 wantsMarkdownacceptsMarkdown 方法。

1if ($request->wantsMarkdown()) {
2 // The client's most preferred content type is text/markdown...
3}
4 
5if ($request->acceptsMarkdown()) {
6 // The client accepts Markdown responses...
7}

PSR-7 请求

PSR-7 标准 规定了 HTTP 消息(包括请求和响应)的接口。如果你想获取 PSR-7 请求实例而不是 Laravel 请求实例,首先需要安装几个库。Laravel 使用 Symfony HTTP Message Bridge 组件将典型的 Laravel 请求和响应转换为兼容 PSR-7 的实现。

1composer require symfony/psr-http-message-bridge
2composer require nyholm/psr7

安装这些库后,你可以在路由闭包或控制器方法中对请求接口进行类型提示,从而获取 PSR-7 请求。

1use Psr\Http\Message\ServerRequestInterface;
2 
3Route::get('/', function (ServerRequestInterface $request) {
4 // ...
5});

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

输入

获取输入

获取所有输入数据

你可以使用 all 方法将所有传入请求的输入数据作为 array 获取。无论传入请求是来自 HTML 表单还是 XHR 请求,此方法均可使用。

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

使用 collect 方法,你可以将所有传入请求的输入数据作为 集合 (collection) 获取。

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

collect 方法还允许你获取传入请求输入的子集作为集合。

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

获取输入值

使用一些简单的方法,你可以从 Illuminate\Http\Request 实例中访问所有用户输入,而无需担心请求使用的是哪种 HTTP 动词。无论使用何种 HTTP 动词,都可以使用 input 方法来获取用户输入。

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

你可以将默认值作为第二个参数传递给 input 方法。如果请求中不存在所请求的输入值,则会返回该值。

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

处理包含数组输入的表单时,请使用“点”符号来访问数组。

1$name = $request->input('products.0.name');
2 
3$names = $request->input('products.*.name');

你可以不带任何参数调用 input 方法,以关联数组的形式获取所有输入值。

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

从查询字符串中获取输入

虽然 input 方法从整个请求负载(包括查询字符串)中获取值,但 query 方法仅从查询字符串中获取值。

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

如果所请求的查询字符串数据不存在,将返回此方法的第二个参数。

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

你可以不带任何参数调用 query 方法,以关联数组的形式获取所有查询字符串值。

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

获取 JSON 输入值

当向应用程序发送 JSON 请求时,只要请求的 Content-Type 头已正确设置为 application/json,你就可以通过 input 方法访问 JSON 数据。你甚至可以使用“点”语法来获取 JSON 数组/对象中嵌套的值。

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

获取可字符串化的输入值

与其将请求输入数据作为原始 string 获取,不如使用 string 方法将请求数据作为 Illuminate\Support\Stringable 实例获取。

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

获取整数输入值

要将输入值作为整数获取,可以使用 integer 方法。此方法将尝试把输入值强制转换为整数。如果输入不存在或转换失败,它将返回你指定的默认值。这对于分页或其他数字输入特别有用。

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

获取布尔输入值

在处理复选框等 HTML 元素时,应用程序可能会收到实际上是字符串的“真值”。例如 "true" 或 "on"。为方便起见,你可以使用 boolean 方法将这些值作为布尔值获取。boolean 方法对于 1, "1", true, "true", "on" 和 "yes" 返回 true。所有其他值都将返回 false

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

获取数组输入值

可以使用 array 方法获取包含数组的输入值。此方法始终会将输入值强制转换为数组。如果请求不包含具有给定名称的输入值,则返回一个空数组。

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

获取日期输入值

为方便起见,可以使用 date 方法将包含日期/时间的输入值作为 Carbon 实例获取。如果请求不包含具有给定名称的输入值,则返回 null

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

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

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

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

获取枚举输入值

对应于 PHP 枚举 的输入值也可以从请求中获取。如果请求不包含具有给定名称的输入值,或者枚举没有与输入值匹配的支持值,则返回 nullenum 方法接受输入值的名称和枚举类作为其第一个和第二个参数。

1use App\Enums\Status;
2 
3$status = $request->enum('status', Status::class);

你还可以提供一个默认值,如果值缺失或无效,则返回该默认值。

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

如果输入值是一个对应于 PHP 枚举的值数组,你可以使用 enums 方法将这些值作为枚举实例数组获取。

1use App\Enums\Product;
2 
3$products = $request->enums('products', Product::class);

通过动态属性获取输入

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

1$name = $request->name;

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

获取输入数据的一部分

如果你需要获取输入数据的子集,可以使用 onlyexcept 方法。这两个方法都接受单个 array 或动态参数列表。

1$input = $request->only(['username', 'password']);
2 
3$input = $request->only('username', 'password');
4 
5$input = $request->except(['credit_card']);
6 
7$input = $request->except('credit_card');

only 方法返回你请求的所有键/值对;但是,它不会返回请求中不存在的键/值对。

检查输入是否存在

你可以使用 has 方法来确定值是否存在于请求中。如果值存在于请求中,has 方法返回 true

1if ($request->has('name')) {
2 // ...
3}

当给定数组时,has 方法将确定是否所有指定的值都存在。

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

如果存在任何指定的值,hasAny 方法返回 true

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

如果值存在于请求中,whenHas 方法将执行给定的闭包。

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

可以向 whenHas 方法传递第二个闭包,如果请求中不存在指定的值,则会执行该闭包。

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

如果你想确定值是否存在于请求中且不是空字符串,可以使用 filled 方法。

1if ($request->filled('name')) {
2 // ...
3}

如果你想确定值是否从请求中缺失或者是空字符串,可以使用 isNotFilled 方法。

1if ($request->isNotFilled('name')) {
2 // ...
3}

当给定数组时,isNotFilled 方法将确定是否所有指定的值都缺失或为空。

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

如果任何指定的值不是空字符串,anyFilled 方法返回 true

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

如果值存在于请求中且不是空字符串,whenFilled 方法将执行给定的闭包。

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

可以向 whenFilled 方法传递第二个闭包,如果指定的值没有“填满”,则会执行该闭包。

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

要确定给定的键是否从请求中缺失,可以使用 missingwhenMissing 方法。

1if ($request->missing('name')) {
2 // ...
3}
4 
5$request->whenMissing('name', function () {
6 // The "name" value is missing...
7}, function () {
8 // The "name" value is present...
9});

合并额外输入

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

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

如果相应的键尚未存在于请求的输入数据中,可以使用 mergeIfMissing 方法将输入合并到请求中。

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

旧输入

Laravel 允许你在下一次请求期间保留本次请求的输入。此功能对于在检测到验证错误后重新填充表单特别有用。但是,如果你正在使用 Laravel 附带的 验证功能,你可能不需要直接手动使用这些会话输入闪存方法,因为 Laravel 的一些内置验证工具会自动调用它们。

将输入闪存到会话

Illuminate\Http\Request 类上的 flash 方法会将当前输入闪存到 会话 (session) 中,以便在用户对应用程序的下一次请求期间可以使用这些输入。

1$request->flash();

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

1$request->flashOnly(['username', 'email']);
2 
3$request->flashExcept('password');

闪存输入并重定向

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

1return redirect('/form')->withInput();
2 
3return redirect()->route('user.create')->withInput();
4 
5return redirect('/form')->withInput(
6 $request->except('password')
7);

获取旧输入

要获取上一次请求的闪存输入,请在 Illuminate\Http\Request 实例上调用 old 方法。old 方法将从 会话 (session) 中提取先前闪存的输入数据。

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

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

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

Cookies

从请求中获取 Cookie

Laravel 框架创建的所有 Cookie 都经过加密并使用身份验证代码签名,这意味着如果它们被客户端更改,将被视为无效。要从请求中获取 Cookie 值,请在 Illuminate\Http\Request 实例上使用 cookie 方法。

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

输入修剪与规范化

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

禁用输入规范化

如果你想为所有请求禁用此行为,可以通过在应用程序的 bootstrap/app.php 文件中调用 $middleware->remove 方法,从应用程序的中间件栈中移除这两个中间件。

1use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
2use Illuminate\Foundation\Http\Middleware\TrimStrings;
3 
4->withMiddleware(function (Middleware $middleware): void {
5 $middleware->remove([
6 ConvertEmptyStringsToNull::class,
7 TrimStrings::class,
8 ]);
9})

如果你想为应用程序的部分请求禁用字符串修剪和空字符串转换,可以在应用程序的 bootstrap/app.php 文件中使用 trimStringsconvertEmptyStringsToNull 中间件方法。这两个方法都接受一个闭包数组,这些闭包应返回 truefalse,以指示是否应跳过输入规范化。

1->withMiddleware(function (Middleware $middleware): void {
2 $middleware->convertEmptyStringsToNull(except: [
3 fn (Request $request) => $request->is('admin/*'),
4 ]);
5 
6 $middleware->trimStrings(except: [
7 fn (Request $request) => $request->is('admin/*'),
8 ]);
9})

文件

获取上传文件

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

1$file = $request->file('photo');
2 
3$file = $request->photo;

你可以使用 hasFile 方法确定请求中是否存在文件。

1if ($request->hasFile('photo')) {
2 // ...
3}

验证成功上传

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

1if ($request->file('photo')->isValid()) {
2 // ...
3}

文件路径和扩展名

UploadedFile 类还包含用于访问文件完全限定路径及其扩展名的方法。extension 方法将尝试根据其内容猜测文件的扩展名。此扩展名可能与客户端提供的扩展名不同。

1$path = $request->photo->path();
2 
3$extension = $request->photo->extension();

其他文件方法

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

存储上传文件

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

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

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

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

如果你不希望自动生成文件名,可以使用 storeAs 方法,它接受路径、文件名和磁盘名称作为参数。

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

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

配置受信任的代理

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

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

1->withMiddleware(function (Middleware $middleware): void {
2 $middleware->trustProxies(at: [
3 '192.168.1.1',
4 '10.0.0.0/8',
5 ]);
6})

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

1->withMiddleware(function (Middleware $middleware): void {
2 $middleware->trustProxies(headers: Request::HEADER_X_FORWARDED_FOR |
3 Request::HEADER_X_FORWARDED_HOST |
4 Request::HEADER_X_FORWARDED_PORT |
5 Request::HEADER_X_FORWARDED_PROTO |
6 Request::HEADER_X_FORWARDED_AWS_ELB
7 );
8})

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

信任所有代理

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

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

配置受信任的主机

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

通常,你应该配置 Web 服务器(如 Nginx 或 Apache)仅向匹配给定主机名的应用程序发送请求。但是,如果你无法直接自定义 Web 服务器,并且需要指示 Laravel 仅响应某些主机名,则可以通过为应用程序启用 Illuminate\Http\Middleware\TrustHosts 中间件来实现。

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

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

默认情况下,来自应用程序 URL 子域的请求也会自动被信任。如果你想禁用此行为,可以使用 subdomains 参数。

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

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

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