--- tags: laravel --- # 網址生成 ![](https://i.imgur.com/xtY18rF.jpg) ## 簡介 Laravel 提供了幾個幫助函式來為應用生成 URL。主要用於在模板和 API 回應中生成 URL 或者在應用的其它部分生成轉址回應 ## 基礎 ### 生成 URLs 幫助函式 url() 可以用於應用的任何一個 URL。生成的 URL 將自動使用當前請求中的協定 (HTTP or HTTPS) 和網域: ``` $post = App\Models\Post::find(1); echo url("/posts/{$post->id}"); // http://example.com/posts/1 ``` ### 取得當前 URL 如果沒有為幫助函式 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(); // Get the full URL for the previous request... echo url()->previous(); ``` 上面的這些方法都可以通過 URL Facade 來訪問: ``` use Illuminate\Support\Facades\URL; echo URL::current(); ``` ## 命名路由的 URLs 幫助函式 route() 可以用於為指定路由生成 URL。命名路由生成的 URL 不與路由上定義的 URL 相耦合。因此就算路由的 URL 有任何改變,都不需要對 route() 呼叫進行任何更改。例如假設你的應用包含以下路由: ``` Route::get('/post/{post}', function () { // })->name('post.show'); ``` 要生成此路由的 URL ,可以像這樣使用幫助函式 route() : ``` echo route('post.show', ['post' => 1]); // http://example.com/post/1 ``` 當然, route() 幫助函式也能傳入多個參數來生成 URLs ``` Route::get('/post/{post}/comment/{comment}', function () { // })->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 模型 你將很常利用 Eloquent 模型的主鍵來生成 URLs。因此,你何不直接傳入 Eloquent 模型作為參數值, route() 將會自動的取用模型的主鍵 `echo route('post.show', ['post' => $post]);` ### 簽名 URLs Laravel 允許你輕鬆地為命名路徑創建「簽名」URL,這些 URL 在查詢字串後附加了「簽名」Hash,允許 Laravel 驗證 URL 自創建以來未被修改過。簽名 URL 對於可公開訪問但需要一層防止 URL 操作的路由特別有用。 例如,你可以使用簽名 URL 來實現通過電子郵件發送給客戶的公開「取消訂閱」連結。要創建命名路由的簽名 URL ,請使用 URL Facade 的 signedRoute() ``` use Illuminate\Support\Facades\URL; return URL::signedRoute('unsubscribe', ['user' => 1]); ``` 如果要生成具有有效期的臨時簽名路由 URL,可以使用 temporarySignedRoute() 。當 Laravel 驗證一個臨時簽名路由 URL,它將確保被加入在簽名 URL 的過期時間戳記還沒有被改動 ``` use Illuminate\Support\Facades\URL; return URL::temporarySignedRoute( 'unsubscribe', now()->addMinutes(30), ['user' => 1] ); ``` ### 驗證簽名路由請求 要驗證傳入請求是否具有有效簽名,你應該對傳入的 Request 呼叫 hasValidSignature() ``` use Illuminate\Http\Request; Route::get('/unsubscribe/{user}', function (Request $request) { if (! $request->hasValidSignature()) { abort(401); } // ... })->name('unsubscribe'); ``` 或者,你可以將 Illuminate\Routing\Middleware\ValidateSignature 中介層分配給路由。如果它不存在,則應該在 HTTP 內核的 routeMiddleware 陣列中為此中介層分配一個鍵 ``` /** * The application's route middleware. * * These middleware may be assigned to groups or used individually. * * @var array */ protected $routeMiddleware = [ 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, ]; ``` 在內核中註冊中介層後,你可以將其附加到路由中。如果傳入請求沒有有效簽名,則中介層將自動返回 403 錯誤回應 ``` Route::post('/unsubscribe/{user}', function (Request $request) { // ... })->name('unsubscribe')->middleware('signed'); ``` ## 控制器行為的 URLs action() 可以為給定的控制器行為生成 URL ``` use App\Http\Controllers\HomeController; $url = action([HomeController::class, 'index']); ``` 如果控制器方法接收路由參數,你可以通過第二個參數傳遞 `$url = action([UserController::class, 'profile'], ['id' => 1]);` ## 預設值 對於某些應用,你可能希望為某些 URL 參數的請求範圍指定預設值。例如假設多數的路由定義了 {locale} 參數 ``` Route::get('/{locale}/posts', function () { // })->name('post.index'); ``` 每次呼叫幫助函數 route() 都要傳入 locale 也是一件很麻煩的事情。 因此使用 URL::defaults() 定義這個參數的預設值,可以讓該參數始終存在當前請求中。然後就能從路由中介層呼叫此方法來訪問當前請求 ``` <?php namespace App\Http\Middleware; use Closure; use Illuminate\Support\Facades\URL; class SetDefaultLocaleForUrls { /** * Handle the incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return \Illuminate\Http\Response */ public function handle($request, Closure $next) { URL::defaults(['locale' => $request->user()->locale]); return $next($request); } } ``` 一旦設置了 locale 參數的預設值,你就不再需要通過幫助函式 route() 生成 URL 時傳遞它的值 ### URL 預設 & 中介層 優先 設定 URL 預設值會干擾 Laravel 處理隱式 Model Binding。因此你應該把你的中介層設到比 Laravel 的 SubstituteBindings 中介層優先。你能夠透過應用 HTTP 內核的 $middlewarePriority 屬性來達成,確保你的中介層擺在 SubstituteBindings 之前 再說明一次, $middlewarePriority 屬性被定義在 Illuminate\Foundation\Http\Kernerl 類別。 你能夠複製它的設定並貼入應用的 HTTP 內核檔案以修改它 ``` /** * The priority-sorted list of middleware. * * This forces non-global middleware to always be in the given order. * * @var array */ protected $middlewarePriority = [ // ... \App\Http\Middleware\SetDefaultLocaleForUrls::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, // ... ]; ```