请求


本文档基于 ChatGPT Plus 自动翻译,虽然学院君审核过,但难免疏漏,如有问题,请在评论区给我留言。

介绍

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/barpath 方法将返回 foo/bar

$uri = $request->path();
检查请求路径/路由

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

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

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

if ($request->routeIs('admin.*')) {
    // ...
}
检索请求URL

要检索传入请求的完整URL,您可以使用 urlfullUrl 方法。 url 方法将返回没有查询字符串的 URL,而 fullUrl 方法则包括查询字符串:

$url = $request->url();
 
$urlWithQueryString = $request->fullUrl();

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

$request->fullUrlWithQuery(['type' => 'phone']);
获取请求主机

您可以通过 hosthttpHostschemeAndHttpHost 方法来检索传入请求的“主机”:

$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();

内容协商

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) {
    // ...
});

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

输入

检索输入

检索所有输入数据

您可以使用 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->input('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 方法以 Illuminate\Support\Stringable 实例的形式获取请求数据:

$name = $request->string('name')->trim();
获取布尔输入值

当处理像复选框这样的HTML元素时,您的应用程序可能会接收实际上是字符串的 “truthy” 值。例如,“true”或“on”。为了方便,您可以使用boolean方法来检索这些值作为布尔值。 boolean方法返回true“1”true“true”“on”“yes”。所有其他值将返回false

$archived = $request->boolean('archived');
检索日期输入值

为了方便起见,可以使用date方法从输入值中提取带有日期/时间的实例,如果请求不包含给定名称的输入值,则返回null

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

第二个和第三个参数会传入date方法,以指定日期的格式和时区,分别为:

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

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

检索枚举输入值

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

use App\Enums\Status;

$status = $request->enum('status', Status::class);
通过动态属性检索输入

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

$name = $request->name;

使用动态属性时,Laravel 将首先在请求有效载荷中查找参数的值。如果未找到,Laravel 将搜索匹配路由参数中的字段。

检索输入数据的一部分

如果您需要检索输入数据的一个子集,可以使用onlyexcept方法。这两种方法均接受一个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'])) {
    // ...
}

whenHas 方法会在请求中拥有值的时候执行给定的闭包:

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

不存在的话,可以传入第二个闭包到 whenHas 方法中,来执行指定的操作

$request->whenHas('name', function (string $input) {
    // "name" 值存在时 ...
}, function () {
    // "name" 值不存在时 ...
});

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

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

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

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

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...
});

为了确定请求中是否缺少给定的键,您可以使用 missingwhenMissing 方法:

if ($request->missing('name')) {
    // ...
}
 
$request->whenMissing('name', function (array $input) {
    // “name”的值缺失...
}, function () {
    // “name”的值存在...
});

合并附加输入

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

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

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

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

旧输入

Laravel 允许你在下一个请求中保留来自一个请求的输入。 此功能特别适用于在检测到验证错误后重新填充表单。 但是,如果您正在使用Laravel 包含的 验证功能,则可能不需要手动使用这些会话输入闪烁方法,因为 Laravel 的一些内置验证功能将自动调用它们。

闪存输入到会话

Illuminate\Http\Request 类的 flash 方法将当前输入闪存到会话中,以便在用户下次请求应用程序时可用:

$request->flash();

你也可以使用 flashOnlyflashExcept 方法来闪存 request 数据的一部分到 session。这些方法对于保持像密码这样的敏感信息不在 session 中是很有用的:

$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 值,请在Illuminate \ Http \ Request实例上使用cookie方法:

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

输入修整和规范化

默认情况下,Laravel 包含在应用程序的全局中间件堆栈中的App\Http\Middleware\TrimStringsIlluminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull中间件。 这些中间件由App\Http\Kernel类列在全局中间件堆栈中。 这些中间件将自动修剪请求中的所有输入字符串字段,并将任何空字符串字段转换为null。 这样,您就不必担心路由和控制器中的这些标准化问题。

禁用输入规范化

如果您想要禁用所有请求的此种行为,可以通过从 App\Http\Kernel 类的 $middleware 属性中移除它们,从而从应用程序的中间件堆栈中移除这两个中间件。

如果您想仅为应用程序的一小部分请求禁用字符串修剪和空字符串转换,则可以使用这两个中间件提供的 skipWhen 方法。 该方法接受一个闭包,该闭包应返回 truefalse 来指示是否应跳过输入规范化。 通常,应在应用程序的 AppServiceProviderboot 方法中调用 skipWhen 方法。

use App\Http\Middleware\TrimStrings;
use Illuminate\Http\Request;
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
 
/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    TrimStrings::skipWhen(function (Request $request) {
        return $request->is('admin/*');
    });
 
    ConvertEmptyStringsToNull::skipWhen(function (Request $request) {
        // ...
    });
}

文件

检索上传的文件

你可以从 Illuminate\Http\Request 实例中使用 file 方法或使用动态属性来检索上传的文件。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 应用程序中包含的 App\Http\Middleware\TrustProxies 中间件,该中间件允许您快速自定义应用程序应该信任的负载均衡器或代理。您的受信代理应当作为此中间件的 $proxies 属性数组列出。除了配置受信代理,你还可以配置应该信任的代理 $headers

<?php
 
namespace App\Http\Middleware;
 
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
 
class TrustProxies extends Middleware
{
    /**
     * The trusted proxies for this application.
     *
     * @var string|array
     */
    protected $proxies = [
        '192.168.1.1',
        '192.168.1.2',
    ];
 
    /**
     * The headers that should be used to detect proxies.
     *
     * @var int
     */
    protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO;
}

如果你正在使用AWS Elastic Load Balancing,你的 $headers 值应该是 Request::HEADER_X_FORWARDED_AWS_ELB。有关在$headers 属性中可以使用的常量的更多信息,查看 Symfony 关于信任代理的文档:trusting proxies

信任所有代理

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

/**
 * The trusted proxies for this application.
 *
 * @var string|array
 */
protected $proxies = '*';

配置互信主机

默认情况下,无论 HTTP 请求的 Host 头部中包含什么内容,Laravel 都会对其作出响应。此外,当 web 请求中生成绝对 URL 时,将会使用 Host 头部的值。

通常,你应该配置你的 web 服务器,例如 Nginx 或 Apache,以只向符合给定主机名的请求发送响应。但是,如果你无法直接定制你的 web 服务器,并且需要指示 Laravel 只响应特定的主机名,你可以通过为你的应用程序启用 App\Http\Middleware\TrustHosts 中间件来实现。

TrustHosts 中间件已经包含在您的应用程序的 $middleware 堆栈中; 然而,您应该取消注释它,以使其成为活动的。 在此中间件的 hosts 方法中,您可以指定应用程序应该响应的主机名。 具有其他 Host 标头值的传入请求将被拒绝:

/**
 * Get the host patterns that should be trusted.
 *
 * @return array<int, string>
 */
public function hosts(): array
{
    return [
        'laravel.test',
        $this->allSubdomainsOfApplicationUrl(),
    ];
}

allSubdomainsOfApplicationUrl 辅助方法将返回与应用程序的 app.url 配置值的所有子域匹配的正则表达式。这个辅助方法提供了一种方便的方式,在构建一个利用通配符子域的应用程序时允许应用程序的所有子域。


点赞 取消点赞 收藏 取消收藏

<< 上一篇: 控制器

>> 下一篇: 响应