Laravel: Route (Định tuyến)
MỤC LỤC BÀI VIẾT:
- Định tuyến cơ bản
- Tham số route
- Route được đặt tên
- Nhóm route
- Ràng buộc model route
- Các route dự phòng
- Giới hạn tỷ lệ
- Giả mạo phương thức form
- Truy cập route hiện thời
- Chia sẻ tài nguyên đa nguồn gốc (CORS)
Định tuyến (route) cơ bản
Các định tuyến (route) Laravel cơ bản nhất chấp nhận một URI và một Closure
, cung cấp một phương pháp xác định các định tuyến rất đơn giản và dễ hiểu như sau:
Route::get('foo', function () {
return 'Hello World';
});
Tệp route mặc định
Tất cả các route Laravel được xác định trong các tệp route của bạn, các tệp này nằm trong thư mục routes. Các tệp này được tải tự động bởi framework. File routes/web.php định nghĩa các route cho giao diện web của bạn. Các route này được gán cho group middleware là web, group này cung cấp các tính năng như trạng thái phiên và bảo vệ CSRF. Các route trong routes/api.php không có trạng thái và được chỉ định tới group middleware api.
Đối với hầu hết các ứng dụng, bạn sẽ bắt đầu bằng cách xác định các route trong file routes/web.php. Các route được xác định trong routes/web.php có thể được truy cập bằng cách nhập URL của route đã xác định trong trình duyệt của bạn. Ví dụ: bạn có thể truy cập route sau đây bằng cách điều hướng đến http://your-app.test/user trong trình duyệt của mình:
Route::get('/user', 'UserController@index');
Các route được xác định trong tệp routes/api.php được lồng trong một nhóm route bởi RouteServiceProvider. Trong nhóm này, tiền tố URI /api được tự động áp dụng nên bạn không cần phải áp dụng thủ công nó cho mọi route trong tệp. Bạn có thể sửa đổi tiền tố và các tùy chọn nhóm định tuyến khác bằng cách sửa đổi lớp RouteServiceProvider của bạn.
Các phương thức route:
Route cho phép bạn đăng ký các route đáp ứng với bất kỳ động từ HTTP nào:
Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);
Đôi khi bạn có thể cần đăng ký một route đáp ứng với nhiều động từ HTTP. Bạn có thể làm như vậy bằng cách sử dụng phương thức match. Hoặc, bạn thậm chí có thể đăng ký một route đáp ứng tất cả các động từ HTTP bằng phương thức any:
Route::match(['get', 'post'], '/', function () {
//
});
Route::any('/', function () {
//
});
Bảo vệ CSRF
Bất kỳ form HTML nào trỏ đến các route POST
, PUT
, PATCH
, hoặc DELETE
mà được định nghĩa trong file route web thì cần được đưa thêm token CSRF, nếu không, yêu cầu sẽ bị từ chối:
<form method="POST" action="/profile">
@csrf
...
</form>
Các route chuyển hướng
Nếu bạn đang xác định một route chuyển hướng đến một URI khác, bạn có thể sử dụng phương thức Route::redirect. Phương thức này cung cấp một lối tắt thuận tiện để bạn không phải xác định toàn bộ route hoặc controller để thực hiện chuyển hướng đơn giản:
Route::redirect('/here', '/there');
Theo mặc định thì Route::redirect sẽ trả về mã trạng thái là 302. Bạn có thể tùy chỉnh mã trạng thái bằng cách sử dụng tham số thứ ba tùy chọn:
Route::redirect('/here', '/there', 301);
Bạn có thể sử dụng phương thức Route::permanentRedirect để trả về mã trạng thái 301:
Route::permanentRedirect('/here', '/there');
Route view
Nếu route của bạn chỉ trả về một view thì bạn có thể sử dụng phương thức Route::view. Giống như phương thức redirect, phương thức này cung cấp một lối tắt đơn giản để bạn không phải xác định toàn bộ route hoặc controller. Phương thức view chấp nhận một URI như là đối số đầu tiên của nó và một tên xem như là đối số thứ hai. Ngoài ra, bạn có thể cung cấp một mảng dữ liệu để chuyển tới view dưới dạng đối số thứ ba tùy chọn:
Route::view('/welcome', 'welcome');
Route::view('/welcome', 'welcome', ['name' => 'Taylor']);
Tham số route
Các tham số bắt buộc
Đôi khi bạn sẽ cần nắm bắt các phân đoạn của URI trong route của mình. Ví dụ: bạn có thể cần lấy ID của người dùng từ URL. Bạn có thể làm như vậy bằng cách xác định các thông số định tuyến:
Route::get('user/{id}', function ($id) {
return 'User '.$id;
});
Bạn có thể xác định nhiều tham số route theo yêu cầu của route của bạn:
Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
//
});
Tham số route luôn được đặt trong dấu ngoặc nhọn {} và phải bao gồm các ký tự chữ cái và không được chứa ký tự -. Thay vì sử dụng ký tự -, hãy sử dụng dấu gạch dưới (_). Các tham số route được đưa vào các callback/controller route dựa trên thứ tự của chúng - tên của các đối số callback/controller không quan trọng.
Các tham số tùy chọn
Đôi khi, bạn có thể cần chỉ định một tham số route, nhưng hãy làm cho sự hiện diện của tham số route đó ở dạng tùy chọn bằng cách đặt một dấu ? sau tên tham số. Đảm bảo cung cấp cho biến tương ứng của route một giá trị mặc định:
Route::get('user/{name?}', function ($name = null) {
return $name;
});
Route::get('user/{name?}', function ($name = 'John') {
return $name;
});
Ràng buộc Biểu thức chính quy
Bạn có thể giới hạn định dạng của các tham số route của mình bằng cách sử dụng phương thức where. Phương thức where chấp nhận tên của tham số và một biểu thức chính quy:
Route::get('user/{name}', function ($name) {
//
})->where('name', '[A-Za-z]+');
Route::get('user/{id}', function ($id) {
//
})->where('id', '[0-9]+');
Route::get('user/{id}/{name}', function ($id, $name) {
//
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);
Các ràng buộc global
Nếu bạn muốn tham số route luôn bị giới hạn bởi một biểu thức chính quy, bạn có thể sử dụng phương thức pattern. Bạn nên tạo các mẫu trong phương thức boot của RouteServiceProvider
:
/**
* Định nghĩa các ràng buộc model route, pattern filters, ...
*
* @return void
*/
public function boot()
{
Route::pattern('id', '[0-9]+');
parent::boot();
}
Sau khi mẫu đã được định nghĩa nó sẽ tự động được áp dụng cho tất cả các route sử dụng tên tham số đó:
Route::get('user/{id}', function ($id) {
// Chỉ được thực thi nếu {id} là số...
});
Dấu gạch chéo chuyển tiếp được mã hóa
Thành phần route Laravel cho phép tất cả các ký tự ngoại trừ /
. Bạn phải cho phép /
là một phần của trình giữ chỗ (placeholder) bằng cách sử dụng biểu thức chính quy điều kiện where:
Route::get('search/{search}', function ($search) {
return $search;
})->where('search', '.*');
Dấu gạch chéo về phía trước (\) được mã hóa chỉ được hỗ trợ trong đoạn route cuối cùng.
Các route được đặt tên
Các route được đặt tên cho phép tạo URL hoặc chuyển hướng thuận tiện cho các route cụ thể. Bạn có thể chỉ định tên cho một route bằng cách đưa phương thức name vào phần định nghĩa:
Route::get('user/profile', function () {
//
})->name('profile');
Bạn cũng có thể chỉ định tên route cho các hành động của controller:
Route::get('user/profile', 'UserProfileController@show')->name('profile');
Tên route phải luôn là duy nhất.
Tạo URL cho các route được đặt tên
Khi bạn đã chỉ định tên cho một route rồi thì bạn có thể sử dụng tên này khi tạo URL hoặc chuyển hướng thông qua phương thức global là route:
// Tạo URLs...
$url = route('profile');
// Tạo Redirects...
return redirect()->route('profile');
Nếu route được đặt tên dùng để định nghĩa các tham số, bạn có thể chuyển các tham số làm đối số thứ hai cho hàm route. Các thông số đã cho sẽ tự động được chèn vào URL ở vị trí chính xác của chúng:
Route::get('user/{id}/profile', function ($id) {
//
})->name('profile');
$url = route('profile', ['id' => 1]);
Nếu bạn chuyển các tham số bổ sung vào mảng, các cặp key/value đó sẽ tự động được thêm vào chuỗi truy vấn của URL được tạo:
Route::get('user/{id}/profile', function ($id) {
//
})->name('profile');
$url = route('profile', ['id' => 1, 'photos' => 'yes']);
//Kết quả là: /user/1/profile?photos=yes
Đôi khi, bạn có thể muốn chỉ định các giá trị mặc định cho toàn bộ yêu cầu cho các tham số URL, chẳng hạn như ngôn ngữ hiện tại. Để thực hiện điều này, bạn có thể sử dụng phương thức
URL::defaults
.
Kiểm tra route hiện thời
Nếu bạn muốn xác định xem yêu cầu hiện tại có được route đến một định tuyến được đặt tên nhất định hay không, bạn có thể sử dụng phương thức named trên một cá thể route. Ví dụ: bạn có thể kiểm tra tên route hiện tại từ middleware route:
/**
* Xử lý một request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request->route()->named('profile')) {
//
}
return $next($request);
}
Nhóm route
Nhóm route cho phép bạn chia sẻ các thuộc tính route, chẳng hạn như middleware hoặc namespace trên một số lượng lớn các route mà không cần xác định các thuộc tính đó trên từng route riêng lẻ. Các thuộc tính dùng chung được chỉ định ở định dạng mảng làm tham số đầu tiên của phương thức.Route::group
Các nhóm lồng nhau cố gắng "hợp nhất" các thuộc tính với nhóm mẹ một cách thông minh. Middleware và điều kiện where được hợp nhất trong khi tên, namespace và tiền tố được thêm vào. Dấu phân cách không gian tên và dấu gạch chéo trong tiền tố URI được tự động thêm vào khi thích hợp.
Middleware
Để gán middleware cho tất cả các route trong một nhóm, bạn có thể sử dụng phương thức middleware trước khi xác định nhóm. Middleware được thực thi theo thứ tự chúng được liệt kê trong mảng:
Route::middleware(['first', 'second'])->group(function () {
Route::get('/', function () {
// Dùng cho first và second Middleware
});
Route::get('user/profile', function () {
// Dùng cho first & second Middleware
});
});
Namespace
Một trường hợp sử dụng phổ biến khác cho các nhóm route là gán cùng một namespace PHP cho một nhóm controller bằng phương thức namespace:
Route::namespace('Admin')->group(function () {
// Các controller bên trong namespace "App\Http\Controllers\Admin"
});
Hãy nhớ rằng, theo mặc định, RouteServiceProvider
bao gồm các tệp route của bạn trong một nhóm namespace, cho phép bạn đăng ký các route controller mà không cần chỉ định tiền tố namespace App\Http\Controllers đầy đủ. Vì vậy, bạn chỉ cần xác định phần namespace đứng sau namespace cơ sở App\Http\Controllers.
Route miền phụ
Nhóm route cũng có thể được sử dụng để xử lý route miền phụ. Miền phụ (subdomain) có thể được chỉ định các tham số route giống như URI route, cho phép bạn nắm bắt một phần của miền phụ để sử dụng trong route hoặc controller. Miền phụ có thể được chỉ định bằng cách gọi phương thức domain trước khi định nghĩa nhóm:
Route::domain('{account}.myapp.com')->group(function () {
Route::get('user/{id}', function ($account, $id) {
//
});
});
Để đảm bảo có thể truy cập được các route miền phụ, bạn nên đăng ký các route miền phụ trước khi đăng ký các route miền gốc. Điều này sẽ ngăn các route miền gốc ghi đè các route miền phụ có cùng đường dẫn URI.
prefix
Phương thức prefix có thể được sử dụng để thiết tiền tố mỗi route trong nhóm với một URI nhất định. Ví dụ: bạn có thể muốn đặt tiền tố cho tất cả các URI route trong nhóm bằng admin
:
Route::prefix('admin')->group(function () {
Route::get('users', function () {
// Tương ứng với URL "/admin/users"
});
});
Tiền tố với tên route
Phương thức name có thể được sử dụng để định tiền tố mỗi tên route trong nhóm với một chuỗi cho trước. Ví dụ, bạn có thể muốn đặt tiền tố cho tất cả các tên của route được nhóm với admin
. Chuỗi đã cho được đặt trước tên route chính xác như nó được chỉ định, vì vậy ta chỉ cần cung cấp ký tự . theo sau trong tiền tố:
Route::name('admin.')->group(function () {
Route::get('users', function () {
// Route được gán tên "admin.users"...
})->name('users');
});
Ràng buộc model route
Khi đưa một ID mô hình vào một route hoặc hành động của bộ điều khiển, bạn thường sẽ truy vấn để truy xuất mô hình tương ứng với ID đó. Liên kết mô hình tuyến Laravel cung cấp một cách thuận tiện để tự động đưa trực tiếp các cá thể mô hình vào các tuyến của bạn. Ví dụ: thay vì nhập ID của người dùng, bạn có thể đưa toàn bộ User
phiên bản mô hình khớp với ID đã cho.
Ràng buộc ngầm
Laravel tự động giải quyết các model Eloquent được xác định trong các route hoặc hành động của controller có tên biến được gợi ý kiểu khớp với tên của đoạn route. Ví dụ:
Route::get('api/users/{user}', function (App\User $user) {
return $user->email;
});
Vì biến $user được gợi ý kiểu là model Eloquent App\User
{user}
và tên biến khớp với phân đoạn URI, nên Laravel sẽ tự động đưa vào cá thể model có ID khớp với giá trị tương ứng từ URI yêu cầu. Nếu không tìm thấy model phù hợp trong cơ sở dữ liệu thì phản hồi HTTP 404 sẽ tự động được tạo.
Tùy chỉnh key
Đôi khi bạn có thể muốn giải quyết các model Eloquent bằng cách sử dụng một cột khác với id
. Để làm như vậy, bạn có thể chỉ định cột trong định nghĩa tham số route:
Route::get('api/posts/{post:slug}', function (App\Post $post) {
return $post;
});
Key và phạm vi tùy chỉnh
Đôi khi, khi ràng buộc ngầm nhiều model Eloquent trong một định nghĩa route duy nhất, bạn có thể muốn phân chia model Eloquent thứ hai sao cho nó phải là con của model Eloquent đầu tiên. Ví dụ: hãy xem xét tình huống này truy xuất một bài đăng trên blog bằng slug cho một người dùng cụ thể:
use App\Post;
use App\User;
Route::get('api/users/{user}/posts/{post:slug}', function (User $user, Post $post) {
return $post;
});
Khi sử dụng ràng buộc ngầm có key tùy chỉnh làm tham số route lồng nhau, Laravel sẽ tự động phân phạm vi truy vấn để truy xuất model lồng nhau bởi cha của nó bằng cách sử dụng các quy ước để đoán tên mối quan hệ trên cha. Trong trường hợp này, người ta sẽ giả định rằng model User có một mối quan hệ được đặt tên là posts
(số nhiều của tên tham số route) có thể được sử dụng để truy xuất model Post.
Tùy chỉnh tên key mặc định
Nếu bạn muốn liên kết model để sử dụng cột cơ sở dữ liệu mặc định khác với id
khi truy xuất một lớp model nhất định, bạn có thể ghi đè phương thức getRouteKeyName trên model Eloquent:
/**
* Lấy key route key cho model.
*
* @return string
*/
public function getRouteKeyName()
{
return 'slug';
}
Ràng buộc tường minh
Để đăng ký một ràng buộc tường minh, hãy sử dụng phương thức model của bộ route để chỉ định lớp cho một tham số nhất định. Bạn nên xác định các ràng buộc model tường mình của mình trong phương thức boot của lớp RouteServiceProvider:
public function boot()
{
parent::boot();
Route::model('user', App\User::class);
}
Tiếp theo, xác định một route có chứa một tham số {user}:
Route::get('profile/{user}', function (App\User $user) {
//
});
Vì ta đã ràng buộc tất cả các tham số {user} với model App\User nên một thể hiện User sẽ được đưa vào route. Vì vậy, ví dụ, một yêu cầu profile/1 chẳng hạn sẽ đưa thể hiện User từ cơ sở dữ liệu có ID là 1.
Nếu không tìm thấy model phù hợp trong cơ sở dữ liệu thì phản hồi HTTP 404 sẽ được tạo tự động.
Tùy chỉnh Logic độ phân giải
Nếu bạn muốn sử dụng logic phân giải của riêng mình, bạn có thể sử dụng phương thức Route::bind. Closure mà bạn truyền tới phương thức bind sẽ nhận được giá trị của đoạn URI và nên trả về thể hiện của lớp mà cần được đưa vào các route:
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
parent::boot();
Route::bind('user', function ($value) {
return App\User::where('name', $value)->firstOrFail();
});
}
Ngoài ra, bạn có thể ghi đè phương thức resolveRouteBinding trên model Eloquent của mình. Phương thức này sẽ nhận giá trị của phân đoạn URI và sẽ trả về thể hiện của lớp sẽ được đưa vào route:
/**
* Retrieve the model for a bound value.
*
* @param mixed $value
* @param string|null $field
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveRouteBinding($value, $field = null)
{
return $this->where('name', $value)->firstOrFail();
}
Các route dự phòng
Sử dụng phương thức Route::fallback bạn có thể định nghĩa một route sẽ được thực thi khi không có route nào khác phù hợp với yêu cầu đến. Thông thường, các yêu cầu chưa được xử lý sẽ tự động hiển thị trang "404" thông qua trình xử lý ngoại lệ của ứng dụng của bạn. Tuy nhiên, vì bạn có thể định nghĩa route fallback trong tệp routes/web.php, nên tất cả các middleware trong nhóm middleware web sẽ áp dụng cho route. Bạn có thể tự do thêm middleware bổ sung vào route này nếu cần:
Route::fallback(function () {
//
});
route dự phòng phải luôn là route cuối cùng mà ứng dụng của bạn đăng ký.
Giới hạn tỷ lệ
Laravel bao gồm một middleware để đánh giá giới hạn quyền truy cập vào các route trong ứng dụng của bạn. Để bắt đầu, hãy gán middleware throttle cho một route hoặc một nhóm route. Middleware throttle chấp nhận hai tham số xác định số lượng yêu cầu tối đa có thể được thực hiện trong một số phút nhất định. Ví dụ: hãy chỉ định rằng người dùng được xác thực có thể truy cập nhóm route sau đây 60 lần một phút:
Route::middleware('auth:api', 'throttle:60,1')->group(function () {
Route::get('/user', function () {
//
});
});
Giới hạn tỷ lệ động
Bạn có thể chỉ định yêu cầu động tối đa dựa trên một thuộc tính của model User được xác thực. Ví dụ: nếu model User chứa một thuộc tính rate_limit thì bạn có thể chuyển tên của thuộc tính cho middleware throttle để nó được sử dụng để tính toán số lượng yêu cầu tối đa:
Route::middleware('auth:api', 'throttle:rate_limit,1')->group(function () {
Route::get('/user', function () {
//
});
});
Khách riêng biệt và giới hạn tỷ lệ người dùng đã xác thực
Bạn có thể chỉ định các giới hạn tỷ lệ khác nhau cho khách và người dùng đã xác thực. Ví dụ: bạn có thể chỉ định tối đa 10
yêu cầu mỗi phút cho khách 60
đối với người dùng đã xác thực:
Route::middleware('throttle:10|60,1')->group(function () {
//
});
Bạn cũng có thể kết hợp chức năng này với các giới hạn tỷ lệ động. Ví dụ: nếu model User chứa thuộc tính rate_limit thì bạn có thể chuyển tên của thuộc tính cho middleware throttle để nó được sử dụng để tính toán số lượng yêu cầu tối đa cho người dùng đã xác thực:
Route::middleware('auth:api', 'throttle:10|rate_limit,1')->group(function () {
Route::get('/user', function () {
//
});
});
Phân đoạn giới hạn tỷ lệ
Thông thường, bạn có thể sẽ chỉ định một giới hạn tỷ lệ cho toàn bộ API của mình. Tuy nhiên, ứng dụng của bạn có thể yêu cầu các giới hạn tỷ lệ khác nhau cho các phân đoạn khác nhau của API. Nếu đúng như vậy, bạn sẽ cần chuyển tên phân đoạn làm đối số thứ ba cho middleware throttle:
Route::middleware('auth:api')->group(function () {
Route::middleware('throttle:60,1,default')->group(function () {
Route::get('/servers', function () {
//
});
});
Route::middleware('throttle:60,1,deletes')->group(function () {
Route::delete('/servers/{id}', function () {
//
});
});
});
Giả mạo phương thức form
Form HTML không hỗ trợ các hành động PUT
, PATCH
hay DELETE
. Vì vậy, khi định nghĩa các route PUT
, PATCH
hay DELETE
mà được gọi là từ một form HTML thì bạn sẽ cần phải thêm một trường ẩn _method
cho form. Giá trị được gửi cùng với trường _method
sẽ được sử dụng làm phương thức yêu cầu HTTP:
<form action="/foo/bar" method="POST">
<input type="hidden" name="_method" value="PUT">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
</form>
Bạn có thể sử dụng chỉ thị Blade @method để tạo input _method:
<form action="/foo/bar" method="POST">
@method('PUT')
@csrf
</form>
Truy cập route hiện thời
Bạn có thể sử dụng các phương thức current
, currentRouteName
và currentRouteAction
trên facade Route
để truy cập thông tin về xử lý các yêu cầu gửi đến route:
$route = Route::current();
$name = Route::currentRouteName();
$action = Route::currentRouteAction();
Tham khảo tài liệu API cho cả lớp cơ bản của facade Route và thể hiện Route để xem lại tất cả các phương thức có thể truy cập.
Chia sẻ tài nguyên đa nguồn gốc (Cross-Origin Resource Sharing - CORS)
Laravel có thể tự động phản hồi các yêu cầu CORS OPTIONS với các giá trị mà bạn định cấu hình. Tất cả các cài đặt CORS có thể được định cấu hình trong tệp cấu hình cors và các yêu cầu TÙY CHỌN sẽ tự động được xử lý bởi middleware HandleCors được đưa vào theo mặc định trong ngăn xếp middleware global.
Để biết thêm thông tin về CORS và các header CORS, vui lòng tham khảo tài liệu web của MDN về CORS.