Laravel: Laravel路由系统详解

最后更新:2026-08-26

路由是 Laravel 的"前台接待"——每个 HTTP 请求先到路由这里报到,再被分派到对应的控制器方法。

1. 你将学到


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 规则,支持命名、分组和参数约束,改动一处自动全局生效。

PHP
// 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 动词提供对应的路由方法:

PHP
// 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 路由

PHP
// Match multiple verbs
Route::match(['get', 'post'], '/shops/search', [ShopController::class, 'search']);

// Any verb
Route::any('/fallback', [FallbackController::class, 'handle']);

▶ 示例:ShopMetrics 基础路由定义

PHP
// 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');

输出:

TEXT 📖 仅展示
// 执行成功

4. 路由参数与约束

(1) 必选参数

PHP
Route::get('/shops/{id}', [ShopController::class, 'show']);
Route::get('/tenants/{tenant}/shops/{shop}', [ShopController::class, 'showForTenant']);

(2) 可选参数

PHP
Route::get('/reports/{type?}', [ReportController::class, 'index']);
// /reports → type = null
// /reports/sales → type = 'sales'

(3) 正则约束

PHP
// 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 路由

PHP
// 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-]+');

输出:

TEXT 📖 仅展示
// 执行成功

5. 路由分组

路由分组让多个路由共享配置(中间件、前缀、命名空间等),避免重复代码。

(1) middleware 分组

PHP
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 分组

PHP
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 分组

PHP
Route::name('admin.')->group(function () {
    Route::get('/users', [AdminUserController::class, 'index'])->name('users');
    // Route name: admin.users
});

(4) 组合分组

PHP
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 多租户路由分组

PHP
// 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);
});

输出:

TEXT 📖 仅展示
// 执行成功

6. 路由命名与 URL 生成

(1) 命名路由

PHP
Route::get('/shops/{id}', [ShopController::class, 'show'])
    ->name('shops.show');

(2) 生成 URL

PHP
// 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 中使用命名路由

HTML
<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>

输出:

TEXT 📖 仅展示
// 执行成功

7. API 路由

routes/api.php 专门用于 API 路由,自动添加 /api 前缀。

(1) apiResource 路由

PHP
// 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 版本控制

PHP
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 路由蓝图

PHP
// 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']);
    });
});

输出:

TEXT 📖 仅展示
// 执行成功

8. 路由匹配流程

100%
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 完整路由蓝图

PHP
// ============================================
// 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']);
});

❓ 常见问题

Q routes/web.php 和 routes/api.php 有什么区别?
A web.php 的路由自动应用 web 中间件组(Session、CSRF、Cookie 加密),适合页面请求;api.php 自动应用 api 中间件组(throttle 限流),URL 自动加 /api 前缀,适合 API 请求。
Q 什么时候用 Route::resource,什么时候手动定义?
A RESTful CRUD 操作用 resource/apiResource 一行搞定;非标准操作(如搜索、导出、批量操作)手动定义额外路由。
Q 路由命名有什么好处?
A 命名路由解耦了 URL 和代码,修改 URL 只需改路由定义,所有 route() 调用自动更新。硬编码 URL 改了就要全局搜索替换,容易遗漏。
Q shallow 路由是什么?
A 嵌套资源默认生成 /shops/{shop}/orders/{order},shallow 让子资源在需要 ID 时才嵌套:show/edit/update/delete 用 /orders/{order},index/create 保留嵌套。减少 URL 层级。
Q 路由太多会影响性能吗?
A 路由数量对性能影响很小,Laravel 使用高效的匹配算法。但如果超过 1000 条路由,建议使用 php artisan route:cache 缓存路由表。
Q 如何查看所有已注册的路由?
A 运行 php artisan route:list 可列出所有路由,包括方法、URI、名称、中间件。加 --path=shops 过滤特定前缀的路由。

📖 小节


📝 作业

  1. 基础题(⭐):为 ShopMetrics 定义以下路由:首页 GET /、关于页 GET /about、联系页 GET+POST /contact,使用命名路由,在浏览器中验证可访问。

  2. 进阶题(⭐⭐):使用路由分组为 ShopMetrics 设计 API v1 路由蓝图,包含 shops、products、orders 三个 apiResource,加上认证中间件和 /api/v1 前缀。

  3. 挑战题(⭐⭐⭐):实现多租户路由分组 /{tenant}/*,编写 TenantResolve 中间件从 URL 解析租户并注入 Request,确保所有子路由都能通过 $request->tenant() 获取当前租户。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏