Laravel: Laravel路由系统详解
最后更新:2026-08-26
路由是 Laravel 的"前台接待"——每个 HTTP 请求先到路由这里报到,再被分派到对应的控制器方法。
1. 你将学到
- 基础路由:Route::get/post/put/patch/delete
- 路由参数与正则约束
- 路由分组:middleware/prefix/name/domain
- 路由命名与 URL 生成
- API 路由与 Route::apiResource()
2. 一个产品经理的真实故事
(1) 痛点:URL 规则混乱导致 SEO 灾难
Alice 为 ShopMetrics 规划了 30 多个页面,但 URL 命名毫无章法:/shop_view.php?id=5、/admin-users-list、/api/getData 混在一起。Google 爬虫抓取效率极低,用户分享链接时地址栏一串问号参数。Bob 后端重构时改了一个 URL,前端 15 处硬编码全部 404。
(2) Laravel 路由的解法
Laravel 路由用声明式语法定义 URL 规则,支持命名、分组和参数约束,改动一处自动全局生效。
// routes/web.php — Clean, named, RESTful routes
Route::get('/shops/{slug}', [ShopController::class, 'show'])
->name('shops.show')
->where('slug', '[a-z0-9-]+');
// Generate URL by name — never hardcode
$url = route('shops.show', ['slug' => 'alice-store']);
// => /shops/alice-store
(3) 收益
Alice 用命名路由统一了 ShopMetrics 的 URL 规则,SEO 排名上升 30%。Bob 重构 URL 只需改路由定义,前端 route() 自动生成新地址,零 404 错误。
3. 基础路由
(1) HTTP 动词路由
Laravel 为每个 HTTP 动词提供对应的路由方法:
// routes/web.php
Route::get('/shops', [ShopController::class, 'index']);
Route::post('/shops', [ShopController::class, 'store']);
Route::put('/shops/{id}', [ShopController::class, 'update']);
Route::patch('/shops/{id}', [ShopController::class, 'updateStatus']);
Route::delete('/shops/{id}', [ShopController::class, 'destroy']);
| HTTP 动词 | 用途 | 幂等性 | 典型操作 |
|---|---|---|---|
| GET | 获取资源 | ✅ | 列表/详情 |
| POST | 创建资源 | ❌ | 新增 |
| PUT | 全量更新 | ✅ | 替换 |
| PATCH | 部分更新 | ✅ | 修改状态 |
| DELETE | 删除资源 | ✅ | 删除 |
(2) match 和 any 路由
// Match multiple verbs
Route::match(['get', 'post'], '/shops/search', [ShopController::class, 'search']);
// Any verb
Route::any('/fallback', [FallbackController::class, 'handle']);
▶ 示例:ShopMetrics 基础路由定义
// routes/web.php
Route::get('/', [HomeController::class, 'index'])->name('home');
Route::get('/about', [AboutController::class, 'index'])->name('about');
Route::get('/pricing', [PricingController::class, 'index'])->name('pricing');
Route::get('/contact', [ContactController::class, 'create'])->name('contact.create');
Route::post('/contact', [ContactController::class, 'store'])->name('contact.store');
输出:
// 执行成功
4. 路由参数与约束
(1) 必选参数
Route::get('/shops/{id}', [ShopController::class, 'show']);
Route::get('/tenants/{tenant}/shops/{shop}', [ShopController::class, 'showForTenant']);
(2) 可选参数
Route::get('/reports/{type?}', [ReportController::class, 'index']);
// /reports → type = null
// /reports/sales → type = 'sales'
(3) 正则约束
// Numeric ID only
Route::get('/shops/{id}', [ShopController::class, 'show'])
->where('id', '[0-9]+');
// Slug format: lowercase, numbers, hyphens
Route::get('/shops/{slug}', [ShopController::class, 'showBySlug'])
->where('slug', '[a-z0-9-]+');
// Multiple constraints
Route::get('/tenants/{tenant}/orders/{id}', [OrderController::class, 'show'])
->where(['tenant' => '[a-z0-9-]+', 'id' => '[0-9]+']);
| 约束方法 | 用法 | 说明 |
|---|---|---|
where() |
单参数正则 | 最灵活 |
whereNumber() |
仅数字 | 等价 where('id', '[0-9]+') |
whereAlpha() |
仅字母 | 等价 where('name', '[a-zA-Z]+') |
whereAlphaNumeric() |
字母+数字 | 等价 where('name', '[a-zA-Z0-9]+') |
whereUuid() |
UUID 格式 | 自动验证 UUID v4 |
▶ 示例:带约束的 ShopMetrics 路由
// routes/web.php
Route::get('/shops/{id}', [ShopController::class, 'show'])
->whereNumber('id');
Route::get('/categories/{slug}', [CategoryController::class, 'show'])
->where('slug', '[a-z0-9-]+');
Route::get('/tenants/{tenant}/dashboard', [DashboardController::class, 'index'])
->where('tenant', '[a-z0-9-]+');
输出:
// 执行成功
5. 路由分组
路由分组让多个路由共享配置(中间件、前缀、命名空间等),避免重复代码。
(1) middleware 分组
Route::middleware(['auth', 'tenant.resolve'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
Route::get('/shops', [ShopController::class, 'index']);
Route::get('/orders', [OrderController::class, 'index']);
});
(2) prefix 分组
Route::prefix('admin')->group(function () {
Route::get('/users', [AdminUserController::class, 'index']);
Route::get('/settings', [AdminSettingController::class, 'index']);
// Full URL: /admin/users, /admin/settings
});
(3) name 分组
Route::name('admin.')->group(function () {
Route::get('/users', [AdminUserController::class, 'index'])->name('users');
// Route name: admin.users
});
(4) 组合分组
Route::prefix('admin')
->middleware(['auth', 'admin'])
->name('admin.')
->group(function () {
Route::get('/users', [AdminUserController::class, 'index'])->name('users');
Route::get('/plans', [AdminPlanController::class, 'index'])->name('plans');
// URL: /admin/users, name: admin.users
});
▶ 示例:ShopMetrics 多租户路由分组
// routes/web.php — Tenant-aware routes
Route::middleware(['auth', 'tenant.resolve'])->prefix('/{tenant}')->group(function () {
Route::get('/dashboard', [TenantDashboardController::class, 'index'])
->name('tenant.dashboard');
Route::resource('/shops', ShopController::class);
Route::resource('/orders', OrderController::class);
Route::resource('/products', ProductController::class);
});
输出:
// 执行成功
6. 路由命名与 URL 生成
(1) 命名路由
Route::get('/shops/{id}', [ShopController::class, 'show'])
->name('shops.show');
(2) 生成 URL
// In Blade templates or controllers
$url = route('shops.show', ['id' => 5]);
// => http://shopmetrics.test/shops/5
// With query parameters
$url = route('shops.index', ['sort' => 'name', 'page' => 2]);
// => http://shopmetrics.test/shops?sort=name&page=2
| 函数 | 用途 | 示例 |
|---|---|---|
route() |
生成命名路由 URL | route('shops.show', 5) |
url() |
生成绝对 URL | url('/shops') |
action() |
根据控制器方法生成 | action([ShopController::class, 'show'], 5) |
▶ 示例:在 Blade 中使用命名路由
<a href="{{ route('shops.show', $shop->id) }}">{{ $shop->name }}</a>
<form action="{{ route('shops.update', $shop->id) }}" method="POST">
@method('PUT')
@csrf
<!-- form fields -->
</form>
输出:
// 执行成功
7. API 路由
routes/api.php 专门用于 API 路由,自动添加 /api 前缀。
(1) apiResource 路由
// routes/api.php
use App\Http\Controllers\Api\ShopController;
Route::apiResource('shops', ShopController::class);
// Generates:
// GET /api/shops → index
// POST /api/shops → store
// GET /api/shops/{shop} → show
// PUT /api/shops/{shop} → update
// DELETE /api/shops/{shop} → destroy
| 方法 | apiResource | resource |
|---|---|---|
| index | ✅ | ✅ |
| create | ❌ | ✅ |
| store | ✅ | ✅ |
| show | ✅ | ✅ |
| edit | ❌ | ✅ |
| update | ✅ | ✅ |
| destroy | ✅ | ✅ |
(2) API 版本控制
Route::prefix('v1')->group(function () {
Route::apiResource('shops', Api\V1\ShopController::class);
Route::apiResource('orders', Api\V1\OrderController::class);
});
Route::prefix('v2')->group(function () {
Route::apiResource('shops', Api\V2\ShopController::class);
});
▶ 示例:ShopMetrics API 路由蓝图
// routes/api.php
Route::middleware('auth:sanctum')->group(function () {
Route::prefix('v1')->name('api.v1.')->group(function () {
Route::apiResource('tenants.shops', Api\V1\TenantShopController::class);
Route::apiResource('shops.orders', Api\V1\ShopOrderController::class);
Route::apiResource('products', Api\V1\ProductController::class);
Route::get('analytics/overview', [Api\V1\AnalyticsController::class, 'overview']);
Route::post('reports/generate', [Api\V1\ReportController::class, 'generate']);
});
});
输出:
// 执行成功
8. 路由匹配流程
flowchart LR
A[HTTP Request] --> B{Match Route?}
B -->|Yes| C[Extract Parameters]
C --> D[Run Middleware]
D --> E[Call Controller Method]
E --> F[Return Response]
B -->|No| G[Fallback Route]
G --> H[404 Not Found]
9. 综合示例:ShopMetrics 完整路由蓝图
// ============================================
// Comprehensive: ShopMetrics complete routes
// Covers: web routes, api routes, groups, constraints
// ============================================
// routes/web.php
Route::get('/', [HomeController::class, 'index'])->name('home');
Route::get('/pricing', [PricingController::class, 'index'])->name('pricing');
Route::middleware('auth')->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
Route::resource('shops', ShopController::class)->whereNumber('shop');
Route::resource('shops.orders', OrderController::class)->shallow();
Route::post('/shops/{shop}/logo', [ShopLogoController::class, 'update'])
->name('shops.logo.update');
});
// routes/api.php
Route::prefix('v1')->middleware('auth:sanctum')->group(function () {
Route::apiResource('shops', Api\ShopController::class);
Route::apiResource('shops.products', Api\ProductController::class)->shallow();
Route::apiResource('shops.orders', Api\OrderController::class)->shallow();
Route::get('analytics/summary', [Api\AnalyticsController::class, 'summary']);
Route::post('reports/generate', [Api\ReportController::class, 'generate']);
});
❓ 常见问题
php artisan route:cache 缓存路由表。php artisan route:list 可列出所有路由,包括方法、URI、名称、中间件。加 --path=shops 过滤特定前缀的路由。📖 小节
- Laravel 为每个 HTTP 动词提供路由方法:get/post/put/patch/delete
- 路由参数支持必选/可选,可用 where() 添加正则约束
- 路由分组让多个路由共享中间件、前缀、命名空间
- 命名路由配合 route() 函数实现 URL 与代码解耦
- apiResource 自动生成 RESTful API 路由(不含 create/edit)
- 路由缓存(route:cache)可提升大量路由的匹配性能
📝 作业
-
基础题(⭐):为 ShopMetrics 定义以下路由:首页 GET /、关于页 GET /about、联系页 GET+POST /contact,使用命名路由,在浏览器中验证可访问。
-
进阶题(⭐⭐):使用路由分组为 ShopMetrics 设计 API v1 路由蓝图,包含 shops、products、orders 三个 apiResource,加上认证中间件和 /api/v1 前缀。
-
挑战题(⭐⭐⭐):实现多租户路由分组
/{tenant}/*,编写 TenantResolve 中间件从 URL 解析租户并注入 Request,确保所有子路由都能通过$request->tenant()获取当前租户。