请求
简介
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 实例提供了一系列方法来检查请求的各种属性,它继承自 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() 和 fullUrl() 方法获取请求的完整 URL。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() 判断动词是否匹配指定的字符串:
$method = $request->method();
if ($request->isMethod('post')) {
// ...
}
请求头
用 header() 方法从 Illuminate\Http\Request 实例中获取请求头。如果该头不存在,返回 null。你也可以传第二个参数作为默认值:
$value = $request->header('X-Header-Name');
$value = $request->header('X-Header-Name', 'default');
hasHeader() 方法判断请求中是否包含指定的请求头:
if ($request->hasHeader('X-Header-Name')) {
// ...
}
方便起见,bearerToken() 方法可以从 Authorization 头中提取 Bearer Token。如果没有这个头,返回空字符串:
$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:
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 Agent 或其他消费 Markdown 响应的客户端服务——可以用 wantsMarkdown() 和 acceptsMarkdown() 方法:
if ($request->wantsMarkdown()) {
// 客户端最偏好的内容类型是 text/markdown...
}
if ($request->acceptsMarkdown()) {
// 客户端接受 Markdown 响应...
}
PSR-7 请求
PSR-7 标准 定义了 HTTP 消息(包括请求和响应)的接口规范。如果你想获取 PSR-7 格式的请求实例而非 Laravel 的 Request,需要先安装几个库。Laravel 使用 Symfony HTTP Message Bridge 组件来将 Laravel 的请求和响应转换为 PSR-7 兼容的实现:
composer require symfony/psr-http-message-bridge
composer require nyholm/psr7
安装好后,在路由闭包或控制器方法中类型提示 PSR-7 请求接口,就能拿到 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) {
// ...
});
获取单个输入值
不管请求是哪种 HTTP 动词,都可以用 input() 方法获取用户输入,不需要关心 GET、POST 的区别:
$name = $request->input('name');
第二个参数是默认值——如果请求中没有这个字段,就返回默认值:
$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 输入值
不想拿原始字符串,想直接拿到 Illuminate\Support\Stringable 实例?用 string() 方法:
$name = $request->string('name')->trim();
获取整数输入值
用 integer() 方法把输入值取成整数。这个方法会自动尝试将值转为整数——如果字段不存在或转换失败,返回你指定的默认值。处理分页参数或其他数字输入时特别有用:
$perPage = $request->integer('per_page');
获取布尔输入值
像复选框这类 HTML 元素,传给后端的"真值"实际上是字符串——比如 "true" 或 "on"。用 boolean() 方法把它们统一转成布尔值:1、"1"、true、"true"、"on"、"yes" 全返回 true,其余返回 false:
$archived = $request->boolean('archived');
注意:复选框的 value 必须是被识别的真值
boolean() 底层用的是 PHP 的 FILTER_VALIDATE_BOOLEAN,只认 "1"、"true"、"on"、"yes" 这四种字符串为 true。
标准 HTML 复选框不设 value 属性时,浏览器默认发 archived=on,刚好命中。但如果你手动设了 value="1" 或其他值,需要确保值在以上列表中,否则就算勾选了也会被当成 false。
<!-- ✅ 默认行为,发 archived=on → boolean() 返回 true -->
<input type="checkbox" name="archived">
<!-- ✅ value="1" 也 OK → boolean() 返回 true -->
<input type="checkbox" name="archived" value="1">
<!-- ❌ value="enabled" 不行 → boolean() 返回 false -->
<input type="checkbox" name="archived" value="enabled">
如果前端传的值不在这四个之列(比如 Vue/React 组件可能发 true/false 布尔值,或自定义字符串),改用 $request->has('archived') 或 $request->input('archived') 手动判断更安全。
获取数组输入值
用 array() 方法获取数组类型的输入值。它始终把输入强转成数组;如果请求中没有该字段,返回空数组:
$versions = $request->array('versions');
获取日期输入值
用 date() 方法获取日期/时间输入值,直接返回 Carbon 实例。如果请求中没有该字段,返回 null:
$birthday = $request->date('birthday');
第二和第三个参数分别指定日期格式和时区:
$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 枚举。如果请求中没有该字段,或者枚举没有匹配的 backing value,则返回 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 会先在请求载荷里找对应参数值;如果没找到,再去匹配的路由参数里找。
获取部分输入数据
only() 和 except() 方法用来取输入数据的子集,接受一个数组或动态参数列表:
$input = $request->only(['username', 'password']);
$input = $request->only('username', 'password');
$input = $request->except(['credit_card']);
$input = $request->except('credit_card');
注意
only() 方法返回你要求的键值对,但不会返回请求中不存在的键。
判断输入是否存在
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) {
// ...
});
同样可以传第二个闭包,在值不满足"已填充"条件时执行:
$request->whenFilled('name', function (string $input) {
// "name" 值已填充...
}, function () {
// "name" 值未填充...
});
missing() 和 whenMissing() 方法判断某个键是否不存在于请求中:
if ($request->missing('name')) {
// ...
}
$request->whenMissing('name', function () {
// "name" 值不存在...
}, function () {
// "name" 值存在...
});
合并额外输入
有时候你需要手动把额外的输入合并到请求已有的输入数据中,用 merge() 方法。如果某个键已经存在,会被新数据覆盖:
$request->merge(['votes' => 0]);
mergeIfMissing() 只在指定的键还不存在于输入数据中时才合并:
$request->mergeIfMissing(['votes' => 0]);
旧输入
Laravel 可以把一次请求中的输入保留到下一次请求中,这在表单验证失败后回填表单时特别有用。不过,如果你用了 Laravel 内置的验证功能,大部分情况下不需要手动调用这些方法——内置验证组件会自动处理。
将输入闪存到 Session
Illuminate\Http\Request 类的 flash() 方法把当前输入闪存到会话中,下一次请求时还能取到:
$request->flash();
flashOnly() 和 flashExcept() 只闪存输入数据的子集,用来避免把密码等敏感信息存进 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')
);
获取旧输入
用 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() 方法获取 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,
]);
})
如果只对部分请求禁用,用 trimStrings 和 convertEmptyStringsToNull 中间件方法,传 except 参数——一个闭包数组,返回 true 表示跳过标准化:
->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')) {
// ...
}
验证文件是否成功上传
除了检查文件是否存在,还可以用 isValid() 方法验证上传过程有没有出错:
if ($request->file('photo')->isValid()) {
// ...
}
文件路径与扩展名
UploadedFile 类还提供了获取文件完整路径和扩展名的方法。extension() 方法会基于文件内容猜测扩展名,可能与客户端提供的扩展名不同:
$path = $request->photo->path();
$extension = $request->photo->extension();
其他文件方法
UploadedFile 实例上还有更多可用方法。详情见 Symfony 的 API 文档。
存储上传文件
存储上传文件通常用你配置好的文件系统。UploadedFile 类有一个 store() 方法,会把上传文件移动到指定磁盘——可以是本地文件系统的某个位置,也可以是 Amazon S3 这样的云存储。
store() 的第一个参数是文件存储路径(相对于文件系统配置的根目录)。这个路径不应包含文件名,因为框架会自动生成一个唯一 ID 作为文件名。
第二个可选参数是目标磁盘的名称。返回值是文件相对于磁盘根目录的路径:
$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() 辅助函数生成的链接偶尔是 HTTP 而非 HTTPS。通常这是因为负载均衡器通过 80 端口把流量转发给应用,应用不知道应该生成安全链接。
解决办法是启用 Illuminate\Http\Middleware\TrustProxies 中间件,用 trustProxies 方法指定应用应该信任哪些负载均衡器或代理。在 bootstrap/app.php 中配置:
->withMiddleware(function (Middleware $middleware): void {
$middleware->trustProxies(at: [
'192.168.1.1',
'10.0.0.0/8',
]);
})
除了配置可信代理的 IP 地址,你还可以配置应该信任哪些代理请求头:
->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
);
})
上面的合在一起时这么写的:(headers开发中基本不用。默认的6个就可以,如果不是非常清楚就不要手动覆盖)
->withMiddleware(function (Middleware $middleware): void {
$middleware->trustProxies(
at: [
'192.168.1.1',
'10.0.0.0/8',
],
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: '*');
})
配置可信主机
默认情况下,Laravel 会响应收到的所有请求,不管 HTTP 请求中 Host 头的内容是什么。此外,生成绝对 URL 时也会用到 Host 头的值。
一般来说,你应当在 Nginx 或 Apache 等 Web 服务器层面配置只把匹配特定主机名的请求发给应用。但如果无法直接配置 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 设为 false:
->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'));
})