请求
HTTP Requests
介绍
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
{
/**
* 存储新用户。
*/
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
{
/**
* 更新指定用户
*/
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 = $request->url();
$urlWithQueryString = $request->fullUrl();
如果你想将查询字符串数据附加到当前 URL,你可以调用 fullUrlWithQuery
方法。此方法将给定数组的查询字符串变量与当前查询字符串合并:
$request->fullUrlWithQuery(['type' => 'phone']);
如果你想获取当前 URL 而不包含给定的查询字符串参数,你可以使用 fullUrlWithoutQuery
方法:
$request->fullUrlWithoutQuery(['type']);
检索请求主机
你可以通过 host
、httpHost
和 schemeAndHttpHost
方法检索传入请求的 “host”:
$request->host();
$request->httpHost();
$request->schemeAndHttpHost();
检索请求方法
method
方法将返回请求的 HTTP 动词。可以使用 isMethod
方法验证 HTTP 动词是否匹配给定字符串:
$method = $request->method();
if ($request->isMethod('post')) {
// ...
}
请求头
你可以使用 Illuminate\Http\Request
实例的 header
方法来检索请求头。如果请求中不存在该头,则会返回 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-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
方法,你可以将传入请求的所有输入数据检索为 集合:
$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');
检索 Stringable 输入值
你可以使用 string
方法而不是将请求输入数据作为原始 string
来检索,以将请求数据作为 Illuminate\Support\Stringable
实例检索:
$name = $request->string('name')->trim();
检索布尔输入值
处理 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) {
// 请求中存在 "name" 值...
}, function () {
// 请求中不存在 "name" 值...
});
如果你想确定请求中某个值存在且不是空字符串,可以使用 filled
方法:
if ($request->filled('name')) {
// ...
}
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 的一些内置验证功能将自动调用它们。
闪存输入到 Session
在 Illuminate\Http\Request
类上的 flash
方法将当前输入闪存到 session,以便在下一次用户请求应用程序时使用:
$request->flash();
你还可以使用 flashOnly
和 flashExcept
方法闪存一部分请求数据到 Session。这些方法对于将敏感信息(如密码)排除在 Session 外的情况下非常有用:
$request->flashOnly(['username', 'email']);
$request->flashExcept('password');
闪存输入后重定向
因为你经常需要将输入闪存到 session 然后重定向到前一个页面,你可以使用 withInput
方法将输入闪存链到重定向上:
return redirect('form')->withInput();
return redirect()->route('user.create')->withInput();
return redirect('form')->withInput(
$request->except('password')
);
检索旧输入
要从前一个请求中检索闪存的输入,可以在 Illuminate\Http\Request
实例上调用 old
方法。old
方法将从 session 中拉取先前闪存的输入数据:
$username = $request->old('username');
Laravel 也提供了一个全局的 old
辅助函数。如果你在 Blade 模板 中显示旧输入,使用 old
辅助函数重新填充表单会更加方便。如果给定字段没有旧输入,则会返回 null
:
<input type="text" name="username" value="{{ old('username') }}">
Cookies
从请求中检索 Cookies
Laravel 框架创建的所有 cookies 都是加密和签名的,这意味着如果客户端更改了它们,它们会被视为无效。要从请求中检索 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',
'192.168.1.2',
]);
})
除了配置可信代理外,你还可以配置应信任的代理头:
->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 弹性负载均衡,你的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: '*');
})
配置可信任的 Host
默认情况下,Laravel 将响应它接收到的所有请求,而不管 HTTP 请求的 Host
标头的内容是什么。此外,在 web 请求期间生成应用程序的绝对 URL 时,将使用 Host
标头的值。
通常情况下,你应该配置你的 Web 服务器(如 Nginx 或 Apache)仅向匹配给定主机名的应用程序发送请求。然而,如果你没有直接自定义你的 Web 服务器的能力,可以通过为你的应用程序启用 Illuminate\Http\Middleware\TrustHosts
中间件,指示 Laravel 仅响应特定主机名的请求。
为了启用 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);
})
原文地址:cndocs/11.x/re...
译文地址:cndocs/11.x/re...