HTTP 请求
简介
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'); // 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
头中检索承载令牌。如果没有这样的头,则将返回空字符串。
$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 Message Bridge 组件将典型的 Laravel 请求和响应转换为 PSR-7 兼容的实现。
composer require symfony/psr-http-message-bridgecomposer 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
方法,您可以将传入请求的所有输入数据作为 集合 检索。
$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');
检索日期输入值
为了方便起见,包含日期/时间的输入值可以使用 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);
通过动态属性检索输入
您还可以使用 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) { // 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') }}">
Cookie
从请求中检索 Cookie
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');
有关 Laravel 中文件存储的更多信息,请查看完整的 文件存储文档。
配置可信代理
当在终止 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 );})
如果您使用的是 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) { $middleware->trustProxies(at: '*');})
配置可信主机
默认情况下,Laravel 将响应它接收到的所有请求,而不管 HTTP 请求的 Host
标头的内容如何。此外,在 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'));})