简介
Laravel 的 Illuminate\Http\Request 类提供了一种面向对象的方式来与应用程序正在处理的当前 HTTP 请求进行交互,以及获取随请求提交的输入、Cookie 和文件。
与请求交互
获取请求
若要通过依赖注入获取当前 HTTP 请求的实例,应在路由闭包或控制器方法中对 Illuminate\Http\Request 类进行类型提示。传入的请求实例将由 Laravel 服务容器 自动注入:
<?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');
// 存储用户……
return redirect('/users');
}
}如前所述,你也可以在路由闭包中对 Illuminate\Http\Request 类进行类型提示。服务容器会在闭包执行时自动将传入的请求注入闭包:
use Illuminate\Http\Request;
Route::get('/', function (Request $request) {
// ...
});依赖注入与路由参数
如果你的控制器方法还需要接收来自路由参数的输入,则应将路由参数列在其他依赖项之后。例如,如果你的路由定义如下:
use App\Http\Controllers\UserController;
Route::put('/user/{id}', [UserController::class, 'update']);你依然可以对 Illuminate\Http\Request 进行类型提示,并通过如下方式定义控制器方法来访问 id 路由参数:
<?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
{
// 更新用户……
return redirect('/users');
}
}请求路径、主机和方法
Illuminate\Http\Request 实例提供了多种方法用于检查传入的 HTTP 请求,并且继承了 Symfony\Component\HttpFoundation\Request 类。下面我们将介绍其中一些最重要的方法。
获取请求路径
path 方法返回请求的路径信息。因此,如果传入请求的目标是 http://example.com/foo/bar,那么 path 方法将返回 foo/bar:
$uri = $request->path();检查请求路径 / 路由
is 方法可让你验证传入请求的路径是否与给定模式匹配。在使用此方法时,你可以将 * 字符用作通配符:
if ($request->is('admin/*')) {
// ...
}使用 routeIs 方法,你可以判断传入请求是否匹配命名路由:
if ($request->routeIs('admin.*')) {
// ...
}获取请求 URL
若要获取传入请求的完整 URL,可以使用 url 或 fullUrl 方法。url 方法返回的 URL 不包含查询字符串,而 fullUrl 方法返回的 URL 包含查询字符串:
$url = $request->url();
$urlWithQueryString = $request->fullUrl();如果你想向当前 URL 追加查询字符串数据,可以调用 fullUrlWithQuery 方法。该方法会将给定的查询字符串变量数组与当前查询字符串进行合并:
$request->fullUrlWithQuery(['type' => 'phone']);如果你想获取不包含指定查询字符串参数的当前 URL,可以使用 fullUrlWithoutQuery 方法:
$request->fullUrlWithoutQuery(['type']);获取请求主机
你可以通过 host、httpHost 和 schemeAndHttpHost 方法获取传入请求的“主机”:
// http://localhost:8000
$request->host(); // localhost
$request->httpHost(); // localhost:8000
$request->schemeAndHttpHost(); // http://localhost:8000获取请求方法
method 方法会返回请求使用的 HTTP 动词。你可以使用 isMethod 方法来验证 HTTP 动词是否与给定字符串匹配:
$method = $request->method();
if ($request->isMethod('post')) {
// ...
}请求头
你可以使用 header 方法从 Illuminate\Http\Request 实例中获取请求头。如果请求中不存在该请求头,将返回 null。不过,header 方法接受一个可选的第二个参数,当请求中不存在该请求头时,将返回此参数的值:
$value = $request->header('X-Header-Name');
$value = $request->header('X-Header-Name', 'default');可以使用 hasHeader 方法来判断请求中是否包含指定的请求头:
if ($request->hasHeader('X-Header-Name')) {
// ...
}为方便起见,可以使用 bearerToken 方法从 Authorization 请求头中获取承载令牌。如果不存在该请求头,将返回空字符串:
$token = $request->bearerToken();请求 IP 地址
可以使用 ip 方法获取向应用程序发起请求的客户端的 IP 地址:
$ipAddress = $request->ip();如果你想获取一个 IP 地址数组,包括代理转发的所有客户端 IP 地址,可以使用 ips 方法。“原始”客户端 IP 地址将位于数组的末尾:
$ipAddresses = $request->ips();一般来说,IP 地址应被视为不受信任的、由用户控制的输入,仅用于信息性目的。
内容协商
Laravel 提供了若干方法,通过 Accept 请求头来检查传入请求所请求的内容类型。首先,getAcceptableContentTypes 方法会返回一个数组,其中包含请求所接受的所有内容类型:
$contentTypes = $request->getAcceptableContentTypes();accepts 方法接受一个内容类型数组,如果请求接受了其中任意一个内容类型,则返回 true。否则,将返回 false:
if ($request->accepts(['text/html', 'application/json'])) {
// ...
}你可以使用 prefers 方法来确定请求最偏好的内容类型(在给定的内容类型数组中)。如果请求未接受所提供的任何内容类型,则返回 null:
$preferred = $request->prefers(['text/html', 'application/json']);由于许多应用程序只提供 HTML 或 JSON,你可以使用 expectsJson 方法快速判断传入请求是否期望得到 JSON 响应:
if ($request->expectsJson()) {
// ...
}如果你需要判断请求是否特别偏好 Markdown,或者在多种内容类型中接受 Markdown(例如在为 AI 代理或其他需要消费 Markdown 响应的客户端提供服务时),可以使用 wantsMarkdown 和 acceptsMarkdown 方法:
if ($request->wantsMarkdown()) {
// 客户端最偏好的内容类型是 text/markdown……
}
if ($request->acceptsMarkdown()) {
// 客户端接受 Markdown 响应……
}PSR-7 请求
PSR-7 标准规定了 HTTP 消息(包括请求和响应)的接口。如果你想获取一个 PSR-7 请求实例而非 Laravel 请求,你首先需要安装几个库。Laravel 使用 Symfony HTTP Message Bridge 组件将常规的 Laravel 请求和响应转换为符合 PSR-7 的实现:
composer require symfony/psr-http-message-bridge
composer require nyholm/psr7安装好这些库后,你可以通过在路由闭包或控制器方法中对请求接口进行类型提示来获取一个 PSR-7 请求:
use Psr\Http\Message\ServerRequestInterface;
Route::get('/', function (ServerRequestInterface $request) {
// ...
});如果你从路由或控制器中返回一个 PSR-7 响应实例,它将自动转换回 Laravel 响应实例,并由框架进行展示。
输入
获取输入
获取所有输入数据
你可以使用 all 方法,以 array 的形式获取传入请求的所有输入数据。无论传入请求是来自 HTML 表单还是 XHR 请求,都可以使用此方法:
$input = $request->all();使用 collect 方法,你可以将传入请求的所有输入数据作为一个集合(collection)来获取:
$input = $request->collect();collect 方法还允许你获取传入请求输入的一个子集,并将其作为集合返回:
$request->collect('users')->each(function (string $user) {
// ...
});获取输入值
使用几个简单的方法,你就可以从 Illuminate\Http\Request 实例中获取所有用户输入,而无需关心请求使用了哪种 HTTP 动词。无论使用何种 HTTP 动词,都可以使用 input 方法获取用户输入:
$name = $request->input('name');你可以将默认值作为第二个参数传递给 input 方法。如果请求中不存在所请求的输入值,则将返回该默认值:
$name = $request->input('name', 'Sally');当处理包含数组输入的表单时,可以使用“点”表示法来访问数组:
$name = $request->input('products.0.name');
$names = $request->input('products.*.name');你可以在不传递任何参数的情况下调用 input 方法,从而以关联数组的形式获取所有输入值:
$input = $request->input();从查询字符串中获取输入
虽然 input 方法会从整个请求载荷(包括查询字符串)中获取值,但 query 方法只会从查询字符串中获取值:
$name = $request->query('name');如果请求的查询字符串值数据不存在,则会返回该方法的第二个参数:
$name = $request->query('name', 'Helen');你可以在不传递任何参数的情况下调用 query 方法,从而以关联数组的形式获取所有查询字符串的值:
$query = $request->query();获取 JSON 输入值
当向应用程序发送 JSON 请求时,只要请求的 Content-Type 头部已正确设置为 application/json,你就可以通过 input 方法访问 JSON 数据。你甚至可以使用“点”语法来获取嵌套在 JSON 数组 / 对象内部的值:
$name = $request->input('user.name');获取可字符串化的输入值
你可以使用 string 方法将请求数据作为 Illuminate\Support\Stringable 的实例返回,而不是将请求的输入数据作为原始的 string 来获取:
$name = $request->string('name')->trim();获取整数输入值
要将输入值作为整数获取,可以使用 integer 方法。此方法会尝试将输入值强制转换为整数。如果输入不存在,它将返回你指定的默认值。这对于分页或其他数值输入尤为有用:
$perPage = $request->integer('per_page');获取布尔值输入
在处理复选框等 HTML 元素时,应用程序可能会接收到实际上是字符串的“真值”。例如,“true”或“on”。为方便起见,你可以使用 boolean 方法将这些值作为布尔值获取。boolean 方法对于 1、"1"、true、"true"、"on" 和 "yes" 会返回 true。其他所有值都会返回 false:
$archived = $request->boolean('archived');获取数组输入值
可以使用 array 方法获取包含数组的输入值。此方法始终会将输入值强制转换为数组。如果请求中不包含给定名称的输入值,则会返回一个空数组:
$versions = $request->array('versions');获取日期输入值
为方便起见,可以使用 date 方法,将包含日期 / 时间的输入值作为 Carbon 实例获取。如果请求中不包含给定名称的输入值,则会返回 null:
$birthday = $request->date('birthday');date 方法接受的第二个和第三个参数可用于分别指定日期的格式和时区:
$elapsed = $request->date('elapsed', '!H:i', 'Europe/Madrid');如果输入值存在但格式无效,则会抛出 InvalidArgumentException;因此,建议在调用 date 方法之前先对输入进行验证。
获取时间间隔输入值
可以使用 interval 方法,将包含持续时间的输入值作为 CarbonInterval 实例获取。如果请求中不包含给定名称的输入值,则会返回 null:
$duration = $request->interval('duration');如果输入值为数字,你可以将单位作为第二个参数提供。单位可以是诸如 second、minute 或 day 这样的字符串,也可以是 Carbon\Unit 枚举实例:
use Carbon\Unit;
$timeout = $request->interval('timeout', 'second');
$delay = $request->interval('delay', Unit::Minute);如果输入值存在但格式无效,则会抛出 InvalidArgumentException;因此,建议在调用 interval 方法之前先对输入进行验证。
获取枚举输入值
对应于PHP 枚举的输入值也可以从请求中获取。如果请求中不包含给定名称的输入值,或者枚举不存在与输入值匹配的底层值,则会返回 null。enum 方法将输入值的名称和枚举类作为其第一和第二个参数:
use App\Enums\Status;
$status = $request->enum('status', Status::class);你也可以提供一个默认值,当值缺失或无效时将返回该默认值:
$status = $request->enum('status', Status::class, Status::Pending);如果输入值是对应于 PHP 枚举的值数组,你可以使用 enums 方法将该值数组作为枚举实例数组来获取:
use App\Enums\Product;
$products = $request->enums('products', Product::class);通过动态属性获取输入
你也可以使用 Illuminate\Http\Request 实例上的动态属性来访问用户输入。例如,如果应用程序的某个表单包含一个 name 字段,你可以这样访问该字段的值:
$name = $request->name;使用动态属性时,Laravel 会首先在请求载荷中查找该参数的值。如果不存在,Laravel 会在匹配路由的参数中搜索该字段。
获取部分输入数据
如果你需要获取输入数据的子集,可以使用 only 和 except 方法。这两个方法都可以接受单个 array 或动态参数列表:
$input = $request->only(['username', 'password']);
$input = $request->only('username', 'password');
$input = $request->except(['credit_card']);
$input = $request->except('credit_card'); only 方法会返回你请求的所有键 / 值对;但是,它不会返回请求中不存在的键 / 值对。
输入存在性
你可以使用 has 方法来确定请求中是否存在某个值。如果请求中存在该值,has 方法将返回 true:
if ($request->has('name')) {
// ...
}当传入数组时,has 方法会判断所有指定的值是否都存在:
if ($request->has(['name', 'email'])) {
// ...
}如果存在任何指定的值,hasAny 方法将返回 true:
if ($request->hasAny(['name', 'email'])) {
// ...
}whenHas 方法会在请求中存在指定值时执行给定的闭包:
$request->whenHas('name', function (string $input) {
// ...
});还可以向 whenHas 方法传递第二个闭包,当请求中不存在指定值时,该闭包将被执行:
$request->whenHas('name', function (string $input) {
// “name”值存在……
}, function () {
// “name”值不存在……
});如果你想判断请求中是否存在某个值,并且该值不是空字符串,可以使用 filled 方法:
if ($request->filled('name')) {
// ...
}如果你想判断请求中是否缺失某个值,或者该值为空字符串,可以使用 isNotFilled 方法:
if ($request->isNotFilled('name')) {
// ...
}当传入一个数组时,isNotFilled 方法会判断所有指定值是否都缺失或为空:
if ($request->isNotFilled(['name', 'email'])) {
// ...
}若任意一个指定值不为空字符串,anyFilled 方法将返回 true:
if ($request->anyFilled(['name', 'email'])) {
// ...
}当请求中存在某个值且该值不为空字符串时,whenFilled 方法会执行给定的闭包:
$request->whenFilled('name', function (string $input) {
// ...
});还可以向 whenFilled 方法传递第二个闭包,当指定值不是“已填充”状态时,该闭包将被执行:
$request->whenFilled('name', function (string $input) {
// “name”值已填充……
}, function () {
// “name”值未填充……
});若要判断请求中是否缺少指定键,可以使用 missing 和 whenMissing 方法:
if ($request->missing('name')) {
// ...
}
$request->whenMissing('name', function () {
// “name”值缺失……
}, function () {
// “name”值存在……
});合并额外输入
有时,你可能需要手动将额外输入合并到请求已有的输入数据中。要实现这一点,可以使用 merge 方法。如果请求中已存在指定的输入键,该键对应的数据将被传入 merge 方法的数据覆盖:
$request->merge(['votes' => 0]);若请求的输入数据中不存在对应键,可以使用 mergeIfMissing 方法将输入合并到请求中:
$request->mergeIfMissing(['votes' => 0]);旧输入
Laravel 允许你在下一次请求时保留上一次请求的输入。此功能在检测到验证错误后重新填充表单时尤为实用。不过,如果你使用的是 Laravel 内置的验证功能,可能无需手动调用这些会话输入闪存方法,因为 Laravel 的部分内置验证机制会自动调用它们。
将输入闪存到会话
Illuminate\Http\Request 类的 flash 方法会将当前输入闪存到会话,使其在用户下一次请求应用时可用:
$request->flash();你也可以使用 flashOnly 和 flashExcept 方法,将部分请求数据闪存到会话。这些方法可用于避免密码等敏感信息存入会话:
$request->flashOnly(['username', 'email']);
$request->flashExcept('password');闪存输入后跳转
由于通常需要先将输入闪存到会话,再跳转到上一页,因此可以通过 withInput 方法轻松将输入闪存操作与跳转操作串联起来:
return redirect('/form')->withInput();
return redirect()->route('user.create')->withInput();
return redirect('/form')->withInput(
$request->except('password')
);获取旧输入
若要获取上一次请求闪存的输入,可在 Illuminate\Http\Request 实例上调用 old 方法。old 方法会从会话中提取之前闪存的输入数据:
$username = $request->old('username');Laravel 还提供了全局 old 辅助函数。若要在Blade 模板中显示旧输入,使用 old 辅助函数重新填充表单会更方便。如果指定字段没有旧输入,将返回 null:
<input type="text" name="username" value="{{ old('username') }}">Cookies
从请求中获取 Cookie
Laravel 框架创建的所有 Cookie 都经过加密,并附带身份验证签名,这意味着如果 Cookie 被客户端篡改,将被视为无效。若要从请求中获取 Cookie 值,可在 Illuminate\Http\Request 实例上使用 cookie 方法:
$value = $request->cookie('name');输入修剪与规范化
默认情况下,Laravel 会在应用的全局中间件栈中包含 Illuminate\Foundation\Http\Middleware\TrimStrings 和 Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull 中间件。这些中间件会自动修剪请求中所有传入的字符串字段,并将所有空字符串字段转换为 null。这样一来,你在路由和控制器中就无需再操心这类规范化问题。
禁用输入规范化
若要对所有请求禁用此行为,可以在应用的 bootstrap/app.php 文件中调用 $middleware->remove 方法,将这两个中间件从应用的中间件栈中移除:
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 文件中使用 trimStrings 和 convertEmptyStringsToNull 中间件方法。这两个方法都接收一个闭包数组,闭包应返回 true 或 false,以表明是否要跳过输入规范化:
->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 类,并提供了多种与文件交互的方法:
$file = $request->file('photo');
$file = $request->photo;你可以使用 hasFile 方法判断请求中是否存在文件:
if ($request->hasFile('photo')) {
// ...
}如果上传的文件是需要在存储前处理的图片,可以使用 image 方法获取 Illuminate\Image\Image 实例;若文件不存在,则返回 null:
$image = $request->image('photo');关于处理图片的更多信息,请参阅完整的图片处理文档。
验证上传是否成功
除了检查文件是否存在,你还可以通过 isValid 方法验证文件上传过程是否出现问题:
if ($request->file('photo')->isValid()) {
// ...
}文件路径与扩展名
UploadedFile 类还包含用于访问文件的完整路径及其扩展名的方法。extension 方法会尝试根据文件内容推测文件的扩展名。该扩展名可能与客户端所提供的扩展名不同:
$path = $request->photo->path();
$extension = $request->photo->extension();其他文件方法
UploadedFile 实例上提供了多种其他方法。如需了解有关这些方法的更多信息,请查阅该类的 API 文档。
存储上传的文件
若要存储上传的文件,通常需要使用已配置好的文件系统之一。UploadedFile 类有一个 store 方法,该方法会将上传的文件移动到您的某个磁盘中,该磁盘可以是本地文件系统上的位置,也可以是类似 Amazon S3 的云存储位置。
store 方法接受文件应存储的路径,该路径相对于文件系统配置的根目录。此路径不应包含文件名,因为系统会自动生成一个唯一 ID 作为文件名。
store 方法还接受一个可选的第二个参数,用于指定存储文件所使用的磁盘名称。该方法将返回文件相对于磁盘根目录的路径:
$path = $request->photo->store('images');
$path = $request->photo->store('images', 's3');如果您不希望自动生成文件名,可以使用 storeAs 方法,该方法接受路径、文件名和磁盘名称作为其参数:
$path = $request->photo->storeAs('images', 'filename.jpg');
$path = $request->photo->storeAs('images', 'filename.jpg', 's3'); 如需了解有关 Laravel 文件存储的更多信息,请查阅完整的文件存储文档。
配置受信任的代理
当您的应用程序运行在终止 TLS / SSL 证书的负载均衡器之后时,您可能会发现在使用 url 辅助函数时,应用程序有时无法生成 HTTPS 链接。通常情况下,这是因为您的应用程序是从负载均衡器通过 80 端口转发流量过来的,且并不知道自己应该生成安全链接。
为了解决此问题,您可以启用 Laravel 应用程序中自带的 Illuminate\Http\Middleware\TrustProxies 中间件,该中间件允许您快速自定义应用程序应当信任的负载均衡器或代理。受信任的代理应在应用程序的 bootstrap/app.php 文件中,通过 trustProxies 中间件方法进行指定:
->withMiddleware(function (Middleware $middleware): void {
$middleware->trustProxies(at: [
'192.168.1.1',
'10.0.0.0/8',
]);
})除了配置受信任的代理,您还可以配置应当受信任的代理请求头:
->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
);
}) 如果您使用的是 AWS Elastic Load Balancing,headers 的值应设为 Request::HEADER_X_FORWARDED_AWS_ELB。如果您的负载均衡器使用的是来自RFC 7239 的标准 Forwarded 请求头,headers 的值则应设为 Request::HEADER_FORWARDED。如需了解可用于 headers 值中的常量的更多信息,请查阅 Symfony 关于信任代理的文档。
信任所有代理
如果您使用的是 Amazon AWS 或其他“云”负载均衡服务提供商,您可能无法得知实际负载均衡器的 IP 地址。在这种情况下,您可以使用 * 来信任所有代理:
->withMiddleware(function (Middleware $middleware): void {
$middleware->trustProxies(at: '*');
})配置受信任的主机
默认情况下,无论 HTTP 请求中的 Host 请求头内容如何,Laravel 都会对所有收到的请求作出响应。此外,在 Web 请求期间生成应用程序的绝对 URL 时,将使用 Host 请求头的值。
通常情况下,您应当配置您的 Web 服务器(例如 Nginx 或 Apache),仅将匹配指定主机名的请求转发给您的应用程序。但是,如果您无法直接自定义 Web 服务器,且需要指示 Laravel 仅响应特定的主机名,则可以通过为应用程序启用 Illuminate\Http\Middleware\TrustHosts 中间件来实现。
若要启用 TrustHosts 中间件,您应在应用程序的 bootstrap/app.php 文件中调用 trustHosts 中间件方法。使用该方法的 at 参数,您可以指定应用程序应响应的主机名。主机名字符串将被视为正则表达式。携带其他 Host 请求头的传入请求将被拒绝:
->withMiddleware(function (Middleware $middleware): void {
$middleware->trustHosts(at: ['^laravel\.test$']);
})默认情况下,来自应用程序 URL 子域名的请求也会被自动信任。如果您希望禁用此行为,可以使用 subdomains 参数:
->withMiddleware(function (Middleware $middleware): void {
$middleware->trustHosts(at: ['^laravel\.test$'], subdomains: false);
})如果您需要访问应用程序的配置文件或数据库来确定受信任的主机,您可以为 at 参数提供一个闭包:
->withMiddleware(function (Middleware $middleware): void {
$middleware->trustHosts(at: fn () => config('app.trusted_hosts'));
})