Skip to content
赞助商赞助商
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待

URL 生成

介绍

Laravel 提供了若干辅助函数,帮助你为应用程序生成 URL。这些辅助函数在构建模板链接、API 响应或生成重定向响应到应用程序的其他部分时非常有用。

基础

生成 URL

可以使用 url 辅助函数为你的应用程序生成任意 URL。生成的 URL 将自动使用当前请求处理的协议(HTTP 或 HTTPS)和主机:

php
$post = App\Models\Post::find(1);

echo url("/posts/{$post->id}");

// http://example.com/posts/1

要生成带有查询字符串参数的 URL,可以使用 query 方法:

php
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

如果提供的查询参数在路径中已存在,将会覆盖原有值:

php
echo url()->query('/posts?sort=latest', ['sort' => 'oldest']);

// http://example.com/posts?sort=oldest

你也可以传递数组作为查询参数。这些值将在生成的 URL 中被正确键值化和编码:

php
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

如果不向 url 辅助函数提供路径,将返回一个 Illuminate\Routing\UrlGenerator 实例,允许你访问当前 URL 的信息:

php
// 获取不带查询字符串的当前 URL...
echo url()->current();

// 获取包含查询字符串的当前 URL...
echo url()->full();

// 获取前一个请求的完整 URL...
echo url()->previous();

// 获取前一个请求的路径...
echo url()->previousPath();

这些方法也可以通过 URL facade 访问:

php
use Illuminate\Support\Facades\URL;

echo URL::current();

命名路由的 URL

可以使用 route 辅助函数为 命名路由 生成 URL。使用命名路由可以不依赖实际定义的 URL 来生成链接,因此即使路由 URL 更改,也无需修改对 route 函数的调用。例如,假设你有如下定义的路由:

php
Route::get('/post/{post}', function (Post $post) {
    // ...
})->name('post.show');

你可以像下面这样生成该路由的 URL:

php
echo route('post.show', ['post' => 1]);

// http://example.com/post/1

当然,route 辅助函数也可以用于具有多个参数的路由:

php
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 中:

php
echo route('post.show', ['post' => 1, 'search' => 'rocket']);

// http://example.com/post/1?search=rocket

Eloquent 模型

你通常会使用 Eloquent 模型 的路由键(通常是主键)来生成 URL。因此,你可以直接将模型作为参数传递。route 辅助函数会自动提取模型的路由键:

php
echo route('post.show', ['post' => $post]);

签名 URL

Laravel 允许你轻松地为命名路由创建“签名”URL。这些 URL 会在查询字符串中附加一个“签名”哈希值,Laravel 可以用它来验证该 URL 是否在创建后未被篡改。签名 URL 特别适用于需要公开访问却又需要防止被修改的路由。

例如,你可以使用签名 URL 来实现一个通过邮件发送的“取消订阅”链接。要创建签名 URL,请使用 URL facade 的 signedRoute 方法:

php
use Illuminate\Support\Facades\URL;

return URL::signedRoute('unsubscribe', ['user' => 1]);

你也可以通过向 signedRoute 方法提供 absolute 参数来排除签名哈希中的域名部分:

php
return URL::signedRoute('unsubscribe', ['user' => 1], absolute: false);

如果你想创建一个在指定时间后失效的临时签名 URL,可以使用 temporarySignedRoute 方法。Laravel 在验证时会确保 URL 中编码的过期时间未过期:

php
use Illuminate\Support\Facades\URL;

return URL::temporarySignedRoute(
    'unsubscribe', now()->addMinutes(30), ['user' => 1]
);

验证签名路由请求

要验证传入请求是否具有有效签名,应在 Illuminate\Http\Request 实例上调用 hasValidSignature 方法:

php
use Illuminate\Http\Request;

Route::get('/unsubscribe/{user}', function (Request $request) {
    if (! $request->hasValidSignature()) {
        abort(401);
    }

    // ...
})->name('unsubscribe');

有时你可能需要允许前端向签名 URL 添加数据,例如进行客户端分页时。因此,你可以使用 hasValidSignatureWhileIgnoring 方法来指定在验证签名时应忽略的查询参数。注意,被忽略的参数可以被任何人修改:

php
if (! $request->hasValidSignatureWhileIgnoring(['page', 'order'])) {
    abort(401);
}

你也可以将 signedIlluminate\Routing\Middleware\ValidateSignature中间件 应用于路由来自动验证签名。如果请求没有有效签名,该中间件会自动返回 403 HTTP 响应:

php
Route::post('/unsubscribe/{user}', function (Request $request) {
    // ...
})->name('unsubscribe')->middleware('signed');

如果你的签名 URL 在哈希中不包含域名,应向中间件提供 relative 参数:

php
Route::post('/unsubscribe/{user}', function (Request $request) {
    // ...
})->name('unsubscribe')->middleware('signed:relative');

响应无效签名路由

当访问已过期的签名 URL 时,默认会显示一个 403 HTTP 状态码的通用错误页面。但你可以在应用的 bootstrap/app.php 文件中为 InvalidSignatureException 异常定义自定义的“渲染”闭包:

php
use Illuminate\Routing\Exceptions\InvalidSignatureException;

->withExceptions(function (Exceptions $exceptions) {
    $exceptions->render(function (InvalidSignatureException $e) {
        return response()->view('errors.link-expired', status: 403);
    });
})

控制器动作的 URL

action 函数可生成给定控制器动作的 URL:

php
use App\Http\Controllers\HomeController;

$url = action([HomeController::class, 'index']);

如果控制器方法接受路由参数,可以将参数的关联数组作为第二个参数传入:

php
$url = action([UserController::class, 'profile'], ['id' => 1]);

流式 URI 对象

Laravel 的 Uri 类提供了一个方便、流式的接口,通过对象来创建和操作 URI。此类封装了底层的 League URI 包,并与 Laravel 的路由系统无缝集成。

你可以通过静态方法轻松创建 Uri 实例:

php
use App\Http\Controllers\UserController;
use App\Http\Controllers\InvokableController;
use Illuminate\Support\Uri;

// 从字符串创建 URI 实例...
$uri = Uri::of('https://example.com/path');

// 为路径、命名路由或控制器动作生成 URI 实例...
$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);

// 从当前请求 URL 创建 URI 实例...
$uri = $request->uri();

创建 URI 实例后,可以流式地进行修改:

php
$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} 参数:

php
Route::get('/{locale}/posts', function () {
    // ...
})->name('post.index');

如果每次调用 route 辅助函数时都要传入 locale 参数将非常繁琐。你可以使用 URL::defaults 方法为该参数定义默认值,在当前请求中始终生效。建议在 路由中间件 中调用此方法,这样可以访问当前请求:

php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\URL;
use Symfony\Component\HttpFoundation\Response;

class SetDefaultLocaleForUrls
{
    /**
     * 处理传入请求
     *
     * @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 参数的默认值,生成 URL 时就不再需要显式传递该值。

URL 默认值与中间件优先级

设置 URL 默认值可能会影响 Laravel 的隐式模型绑定处理。因此,应当 优先执行 设置 URL 默认值的中间件,确保其在 Laravel 自带的 SubstituteBindings 中间件之前运行。可以在应用的 bootstrap/app.php 文件中通过 priority 方法实现:

php
->withMiddleware(function (Middleware $middleware) {
    $middleware->prependToPriorityList(
        before: \Illuminate\Routing\Middleware\SubstituteBindings::class,
        prepend: \App\Http\Middleware\SetDefaultLocaleForUrls::class,
    );
})