如何设置ThinkPHP的API路由规则?
- 内容介绍
- 文章标签
- 相关推荐
本文共计971个文字,预计阅读时间需要4分钟。
ThinkPHP的API开发,主要涉及以下步骤:
API 路由必须显式声明 HTTP 方法
写 Route::rule('api/login', 'Api/Login/login') 是危险的:它默认响应所有 method(GET/POST/PUT/DELETE),会跳过 CSRF 验证中间件的 method 检查逻辑,导致 POST 请求绕过防护直接进控制器。
- 正确做法是用语义化方法:
Route::post('api/login', 'Api/Login/login') - 若需同时支持 POST 和 PUT(如创建/更新共用接口),必须显式传入:
Route::rule('api/user', 'Api/User/save', 'POST|PUT') -
Route::any()仅用于调试或 WebHook 类极少数场景,线上 API 路由禁用
Route::get 和 Route::rule 的行为差异
Route::get 是快捷封装,自带 GET 限定 + 严格模式;Route::rule 是底层通用接口,不加 method 参数就等于 '*',且默认不启用正则约束能力。
-
Route::get('user/<id>', 'User/read')</id>→ 只响应 GET,<id></id>默认匹配非斜杠字符([^/]+) -
Route::rule('user/<id>', 'User/read')</id>→ 响应所有 method,且<id></id>不自动约束格式,Request::param('id')可能为空或类型错误 - 需要正则约束时,
Route::rule必须配合->pattern(['id' => '\d+']);而Route::get同样支持该链式调用
动态参数命名与控制器方法签名必须严格一致
URL 中的 <id></id> 不会自动映射到控制器方法的 $uid 或 $ID —— 名称、顺序、类型都得对上,否则拿不到值,PHP 8+ 还会抛 TypeError。
立即学习“PHP免费学习笔记(深入)”;
- 路由定义:
Route::get('blog/<year>/<month>/<id>', 'Blog/detail')</id></month></year> - 控制器方法必须是:
public function detail($year, $month, $id)(顺序不能换) - 若方法声明为
public function detail(int $year, string $month, int $id),则<year></year>和<id></id>必须用->pattern(['year' => '\d+', 'id' => '\d+'])约束,否则 PHP 类型声明校验失败 - 含特殊字符的参数(如 Base64 token)要用
\S+:->pattern(['token' => '\S+']),避免被 / 截断
资源路由不适合纯 API 场景,建议手动拆解
Route::resource('users', 'User') 生成的 7 条路由里,create 和 edit 是面向 HTML 表单的,纯 JSON API 完全不需要,还可能暴露无意义路径被扫描器误触。
- 更可控的做法是手动注册精简版:
Route::get('api/v1/users', 'Api/User/index')Route::post('api/v1/users', 'Api/User/save')Route::get('api/v1/users/<id>', 'Api/User/read')</id>Route::put('api/v1/users/<id>', 'Api/User/update')</id>Route::delete('api/v1/users/<id>', 'Api/User/delete')</id>- 注意前缀
api/v1/必须写在每条规则里,或用Route::prefix('api/v1')->group(...)包裹,不能指望Route::resource()自动识别版本段
最容易被忽略的是:改完 app/route/app.php 后,不执行 php think route:clear,缓存路由不会更新,所有改动都白写。另外,闭包路由(function() { ... })无法被 php think route:list 显示,调试时几乎等于盲开,API 路由一律用控制器地址字符串。
本文共计971个文字,预计阅读时间需要4分钟。
ThinkPHP的API开发,主要涉及以下步骤:
API 路由必须显式声明 HTTP 方法
写 Route::rule('api/login', 'Api/Login/login') 是危险的:它默认响应所有 method(GET/POST/PUT/DELETE),会跳过 CSRF 验证中间件的 method 检查逻辑,导致 POST 请求绕过防护直接进控制器。
- 正确做法是用语义化方法:
Route::post('api/login', 'Api/Login/login') - 若需同时支持 POST 和 PUT(如创建/更新共用接口),必须显式传入:
Route::rule('api/user', 'Api/User/save', 'POST|PUT') -
Route::any()仅用于调试或 WebHook 类极少数场景,线上 API 路由禁用
Route::get 和 Route::rule 的行为差异
Route::get 是快捷封装,自带 GET 限定 + 严格模式;Route::rule 是底层通用接口,不加 method 参数就等于 '*',且默认不启用正则约束能力。
-
Route::get('user/<id>', 'User/read')</id>→ 只响应 GET,<id></id>默认匹配非斜杠字符([^/]+) -
Route::rule('user/<id>', 'User/read')</id>→ 响应所有 method,且<id></id>不自动约束格式,Request::param('id')可能为空或类型错误 - 需要正则约束时,
Route::rule必须配合->pattern(['id' => '\d+']);而Route::get同样支持该链式调用
动态参数命名与控制器方法签名必须严格一致
URL 中的 <id></id> 不会自动映射到控制器方法的 $uid 或 $ID —— 名称、顺序、类型都得对上,否则拿不到值,PHP 8+ 还会抛 TypeError。
立即学习“PHP免费学习笔记(深入)”;
- 路由定义:
Route::get('blog/<year>/<month>/<id>', 'Blog/detail')</id></month></year> - 控制器方法必须是:
public function detail($year, $month, $id)(顺序不能换) - 若方法声明为
public function detail(int $year, string $month, int $id),则<year></year>和<id></id>必须用->pattern(['year' => '\d+', 'id' => '\d+'])约束,否则 PHP 类型声明校验失败 - 含特殊字符的参数(如 Base64 token)要用
\S+:->pattern(['token' => '\S+']),避免被 / 截断
资源路由不适合纯 API 场景,建议手动拆解
Route::resource('users', 'User') 生成的 7 条路由里,create 和 edit 是面向 HTML 表单的,纯 JSON API 完全不需要,还可能暴露无意义路径被扫描器误触。
- 更可控的做法是手动注册精简版:
Route::get('api/v1/users', 'Api/User/index')Route::post('api/v1/users', 'Api/User/save')Route::get('api/v1/users/<id>', 'Api/User/read')</id>Route::put('api/v1/users/<id>', 'Api/User/update')</id>Route::delete('api/v1/users/<id>', 'Api/User/delete')</id>- 注意前缀
api/v1/必须写在每条规则里,或用Route::prefix('api/v1')->group(...)包裹,不能指望Route::resource()自动识别版本段
最容易被忽略的是:改完 app/route/app.php 后,不执行 php think route:clear,缓存路由不会更新,所有改动都白写。另外,闭包路由(function() { ... })无法被 php think route:list 显示,调试时几乎等于盲开,API 路由一律用控制器地址字符串。

