Http请求
导言
Laravel 的 Illuminate\Http\Request 类提供了一种面向对象的方式来与当前正在由您的应用程序处理的 HTTP 请求进行交互,以及检索随请求提交的输入、cookies 和文件。
与请求交互
访问请求
要通过依赖注入获取当前 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');
// Store the user...
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
{
// Update the user...
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 = $request->url();
$urlWithQueryString = $request->fullUrl();
如果您想将查询字符串数据附加到当前 URL,您可以调用 fullUrlWithQuery 方法。此方法将给定的查询字符串变量数组与当前查询字符串合并:
$request->fullUrlWithQuery(['type' => 'phone']);
如果您想获取不带给定查询字符串参数的当前 URL,您可以使用 fullUrlWithoutQuery 方法:
$request->fullUrlWithoutQuery(['type']);
检索请求主机
您可以通过 host、httpHost 和 schemeAndHttpHost 方法检索传入请求的“主机”:
$request->host();
$request->httpHost();
$request->schemeAndHttpHost();
检索请求方法
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 头中检索 bearer 令牌。如果不存在此类头,将返回一个空字符串:
$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()) {
// ...
}
PSR-7 请求
PSR-7 标准 指定了 HTTP 消息的接口,包括请求和响应。如果您想获取 PSR-7 请求的实例而不是 Laravel 请求,您首先需要安装几个库。Laravel 使用 Symfony HTTP 消息桥 组件将典型的 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) {
// ...
});
输入
检索输入
检索所有输入数据
您可以使用 all 方法将传入请求的所有输入数据作为数组检索。无论传入请求是来自 HTML 表单还是 XHR 请求,都可以使用此方法:
$input = $request->all();
使用 collect 方法,您可以将传入请求的所有输入数据作为 集合 检索:
$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 的实例检索,而不是将其作为原始字符串检索:
$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 方法之前验证输入。
检索枚举输入值
与 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 方法。这两个方法都接受一个数组或一个动态参数列表:
$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) {
// The "name" value is present...
}, function () {
// The "name" value is not present...
});
如果您想确定一个值是否在请求上存在并且不是一个空字符串,您可以使用 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) {
// The "name" value is filled...
}, function () {
// The "name" value is not filled...
});
要确定请求中是否缺少给定键,您可以使用 missing 和 whenMissing 方法:
if ($request->missing('name')) {
// ...
}
$request->whenMissing('name', function () {
// The "name" value is missing...
}, function () {
// The "name" value is present...
});
合并额外输入
有时您可能需要手动将额外输入合并到请求的现有输入数据中。为此,您可以使用 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
从请求中检索 Cookies
由 Laravel 框架创建的所有 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) {
$middleware->remove([
ConvertEmptyStringsToNull::class,
TrimStrings::class,
]);
})
如果您想为应用程序的部分请求禁用字符串修剪和空字符串转换,您可以使用应用程序的 bootstrap/app.php 文件中的 trimStrings 和 convertEmptyStringsToNull 中间件方法。这两个方法都接受一个闭包数组,这些闭包应返回 true 或 false 以指示是否应跳过输入规范化:
->withMiddleware(function (Middleware $middleware) {
$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')) {
// ...
}
验证成功上传
除了检查文件是否存在之外,您还可以通过 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');
配置受信任的代理
当您的应用程序在终止 TLS / SSL 证书的负载均衡器后面运行时,您可能会注意到在使用 url 助手时,您的应用程序有时不会生成 HTTPS 链接。通常这是因为您的应用程序正在从您的负载均衡器接收端口 80 上的流量,并且不知道它应该生成安全链接。
为了解决这个问题,您可以启用 Laravel 应用程序中包含的 Illuminate\Http\Middleware\TrustProxies 中间件,这使您可以快速自定义您的应用程序应信任的负载均衡器或代理。您的受信任代理应该使用应用程序的 bootstrap/app.php 文件中的 trustProxies 中间件方法来指定:
->withMiddleware(function (Middleware $middleware) {
$middleware->trustProxies(at: [
'192.168.1.1',
'10.0.0.0/8',
]);
})
除了配置受信任的代理之外,您还可以配置应受信任的代理头:
->withMiddleware(function (Middleware $middleware) {
$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
);
})
headers 值应该是 Request::HEADER_X_FORWARDED_AWS_ELB。如果您的负载均衡器使用 RFC 7239 中的标准 Forwarded 头,headers 值应该是 Request::HEADER_FORWARDED。有关可在 headers 值中使用的常量的更多信息,请查阅 Symfony 关于 信任代理 的文档。信任所有代理
如果您正在使用 Amazon AWS 或其他“云”负载均衡器提供商,您可能不知道您的实际均衡器的 IP 地址。在这种情况下,您可以使用 * 来信任所有代理:
->withMiddleware(function (Middleware $middleware) {
$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) {
$middleware->trustHosts(at: ['laravel.test']);
})
默认情况下,来自应用程序 URL 子域的请求也会自动受信任。如果您想禁用此行为,您可以使用 subdomains 参数:
->withMiddleware(function (Middleware $middleware) {
$middleware->trustHosts(at: ['laravel.test'], subdomains: false);
})
如果您需要访问应用程序的配置文件或数据库来确定您的受信任主机,您可以向 at 参数提供一个闭包:
->withMiddleware(function (Middleware $middleware) {
$middleware->trustHosts(at: fn () => config('app.trusted_hosts'));
})