Laravel 提供了多个辅助函数,帮助您为应用程序生成 URL。这些辅助函数主要在您的模板和 API 响应中构建链接时,或者在为应用程序的其他部分生成重定向响应时非常有用。
url 辅助函数可用于为你的应用生成任意 URL。生成的 URL 将自动使用应用正在处理的当前请求中的方案 (HTTP 或 HTTPS) 和主机:
$post = App\Models\Post::find(1);
echo url("/posts/{$post->id}");
// http://example.com/posts/1要生成一个带查询字符串参数的 URL,你可以使用 query 方法:
echo url()->query('/posts', ['search' => 'Laravel']);
// https://example.com/posts?search=Laravel
echo url()->query('/posts?sort=latest', ['search' => 'Laravel']);
// http://example.com/posts?sort=latest&search=Laravel提供已存在于路径中的查询字符串参数,将覆盖它们现有的值:
echo url()->query('/posts?sort=latest', ['sort' => 'oldest']);
// http://example.com/posts?sort=oldest值数组也可以作为查询参数传递。这些值将在生成的 URL 中被正确地键控和编码:
echo $url = url()->query('/posts', ['columns' => ['title', 'body']]);
// http://example.com/posts?columns%5B0%5D=title&columns%5B1%5D=body
echo urldecode($url);
// http://example.com/posts?columns[0]=title&columns[1]=body如果没有为 url 辅助函数提供路径,则会返回一个 Illuminate\Routing\UrlGenerator 实例,允许您访问有关当前 URL 的信息:
// Get the current URL without the query string...
echo url()->current();
// Get the current URL including the query string...
echo url()->full();这些方法中的每一个也可以通过 URL 门面访问:
use Illuminate\Support\Facades\URL;
echo URL::current();有时,了解用户从哪个 URL 访问过来会很有帮助。 你可以通过 url 助手的 previous 和 previousPath 方法访问上一级 URL:
// Get the full URL for the previous request...
echo url()->previous();
// Get the path for the previous request...
echo url()->previousPath();或者,通过 会话,你可以将上一个 URL 作为 流畅 URI 实例进行访问:
use Illuminate\Http\Request;
Route::post('/users', function (Request $request) {
$previousUri = $request->session()->previousUri();
// ...
});也可以通过会话检索前一个访问的 URL 的路由名称:
$previousRoute = $request->session()->previousRoute();route 辅助函数可用于生成指向 命名路由 的 URL。命名路由允许你生成 URL,而无需与路由上定义的实际 URL 耦合。因此,如果路由的 URL 发生变化,则无需对你对 route 函数的调用进行任何更改。例如,设想你的应用程序包含如下定义的路由:
Route::get('/post/{post}', function (Post $post) {
// ...
})->name('post.show');要为此路由生成 URL,您可以使用 route 辅助函数,如下所示:
echo route('post.show', ['post' => 1]);
// http://example.com/post/1当然,route 辅助函数也可以用于为带多个参数的路由生成 URL:
Route::get('/post/{post}/comment/{comment}', function (Post $post, Comment $comment) {
// ...
})->name('comment.show');
echo route('comment.show', ['post' => 1, 'comment' => 3]);
// http://example.com/post/1/comment/3任何不对应于路由定义参数的额外数组元素,都将被添加到 URL 的查询字符串中:
echo route('post.show', ['post' => 1, 'search' => 'rocket']);
// http://example.com/post/1?search=rocket您通常会使用 Eloquent 模型 的路由键(通常是主键)来生成 URL。因此,您可以将 Eloquent 模型作为参数值传递。route 助手函数将自动提取模型的路由键:
echo route('post.show', ['post' => $post]);Laravel 允许你轻松创建指向具名路由的“签名”URL。这些 URL 会在查询字符串中附加一个“签名”哈希,使得 Laravel 能够验证该 URL 自创建以来未被修改。签名 URL 对于公开可访问但需要一层保护以防止 URL 篡改的路由尤其有用。
例如,你可能使用签名 URL 来实现一个公共的“取消订阅”链接,该链接会通过电子邮件发送给你的客户。要为命名路由创建签名 URL,使用 URL 外观的 signedRoute 方法:
use Illuminate\Support\Facades\URL;
return URL::signedRoute('unsubscribe', ['user' => 1]);您可以从签名 URL 哈希中排除域名,方法是向 signedRoute 方法提供 absolute 参数:
return URL::signedRoute('unsubscribe', ['user' => 1], absolute: false);如果您想生成一个在指定时间后过期的临时签名路由 URL,您可以使用 temporarySignedRoute 方法。当 Laravel 验证一个临时签名路由 URL 时,它将确保编码到签名 URL 中的过期时间戳尚未过期:
use Illuminate\Support\Facades\URL;
return URL::temporarySignedRoute(
'unsubscribe', now()->addMinutes(30), ['user' => 1]
);为了验证传入请求具有有效的签名,你应该在传入的 Illuminate\Http\Request 实例上调用 hasValidSignature 方法:
use Illuminate\Http\Request;
Route::get('/unsubscribe/{user}', function (Request $request) {
if (! $request->hasValidSignature()) {
abort(401);
}
// ...
})->name('unsubscribe');有时,你可能需要允许你的应用程序前端向一个签名 URL 添加数据,例如在执行客户端分页时。因此,你可以指定在使用 hasValidSignatureWhileIgnoring 方法验证签名 URL 时应该被忽略的请求查询参数。请记住,忽略参数允许任何人修改请求中的那些参数:
if (! $request->hasValidSignatureWhileIgnoring(['page', 'order'])) {
abort(401);
}不使用传入请求实例验证签名 URL,你可以将 signed (Illuminate\Routing\Middleware\ValidateSignature) 中间件 分配给路由。如果传入请求没有有效的签名,该中间件将自动返回 403 HTTP 响应:
Route::post('/unsubscribe/{user}', function (Request $request) {
// ...
})->name('unsubscribe')->middleware('signed');如果您的签名 URL 不包含 URL 哈希中的域名,您应该向中间件提供 relative 参数:
Route::post('/unsubscribe/{user}', function (Request $request) {
// ...
})->name('unsubscribe')->middleware('signed:relative');当有人访问已过期的签名 URL 时,他们将收到一个通用的错误页面,对应 403 HTTP 状态码。但是,您可以通过在应用程序的 bootstrap/app.php 文件中为 InvalidSignatureException 异常定义一个自定义的 "render" 闭包来定制此行为:
use Illuminate\Routing\Exceptions\InvalidSignatureException;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->render(function (InvalidSignatureException $e) {
return response()->view('errors.link-expired', status: 403);
});
})action 函数会为给定的控制器动作生成一个 URL:
use App\Http\Controllers\HomeController;
$url = action([HomeController::class, 'index']);如果控制器方法接受路由参数,您可以将一个路由参数的关联数组作为第二个参数传递给该函数:
$url = action([UserController::class, 'profile'], ['id' => 1]);Laravel's Uri class provides a convenient and fluent interface for creating and manipulating URIs via objects. This class wraps the functionality provided by the underlying League URI package and integrates seamlessly with Laravel's routing system.
你可以创建一个 Uri 实例,通过静态方法即可轻松创建:
use App\Http\Controllers\UserController;
use App\Http\Controllers\InvokableController;
use Illuminate\Support\Uri;
// Generate a URI instance from the given string...
$uri = Uri::of('https://example.com/path');
// Generate URI instances to paths, named routes, or controller actions...
$uri = Uri::to('/dashboard');
$uri = Uri::route('users.show', ['user' => 1]);
$uri = Uri::signedRoute('users.show', ['user' => 1]);
$uri = Uri::temporarySignedRoute('user.index', now()->addMinutes(5));
$uri = Uri::action([UserController::class, 'index']);
$uri = Uri::action(InvokableController::class);
// Generate a URI instance from the current request URL...
$uri = $request->uri();
// Generate a URI instance from the previous request URL...
$uri = $request->session()->previousUri();一旦你拥有一个 URI 实例,你就可以流畅地修改它:
$uri = Uri::of('https://example.com')
->withScheme('http')
->withHost('test.com')
->withPort(8000)
->withPath('/users')
->withQuery(['page' => 2])
->withFragment('section-1');有关使用流式 URI 对象的更多信息,请查阅 URI 文档。
对于某些应用程序,您可能希望为某些 URL 参数指定请求范围的默认值。例如,假设您的许多路由定义了一个 {locale} 参数:
Route::get('/{locale}/posts', function () {
// ...
})->name('post.index');每次调用 route 辅助函数时都传递 locale 会很繁琐。因此,你可以使用 URL::defaults 方法为此参数定义一个默认值,该值将在当前请求期间始终应用。你可能希望从 路由中间件 中调用此方法,以便你可以访问当前请求:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\URL;
use Symfony\Component\HttpFoundation\Response;
class SetDefaultLocaleForUrls
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
URL::defaults(['locale' => $request->user()->locale]);
return $next($request);
}
}一旦为 locale 参数设置了默认值,您就不再需要在通过 route 助手生成 URL 时传递其值。
设置 URL 默认值可能会干扰 Laravel 对隐式模型绑定的处理。因此,您应该优先处理您的中间件,以便在 Laravel 自己的 SubstituteBindings 中间件之前执行设置 URL 默认值的中间件。您可以通过使用您应用程序的 bootstrap/app.php 文件中的 priority 中间件方法来实现这一点:
->withMiddleware(function (Middleware $middleware): void {
$middleware->prependToPriorityList(
before: \Illuminate\Routing\Middleware\SubstituteBindings::class,
prepend: \App\Http\Middleware\SetDefaultLocaleForUrls::class,
);
})