1. 引言
随着前后端分离架构的普及,API 接口开发已成为 PHP 开发者必须掌握的技能。ThinkPHP 6 作为国内流行的 PHP 框架,提供了简洁高效的 API 开发支持。本文将以一个用户管理模块为例,从环境准备、路由配置、控制器编写到接口测试,完整演示 ThinkPHP 6 API 接口的开发流程。
2. 环境准备与项目创建
2.1 环境要求
- PHP >= 7.2.5(推荐 7.4 或 8.0)
- Composer
- MySQL 5.7+ 或 8.0
2.2 创建项目
使用 Composer 创建 ThinkPHP 6 项目:
bash
composer create-project topthink/think tp6-api
cd tp6-api
2.3 安装扩展
安装 API 开发常用的扩展包:
bash
composer require topthink/think-orm
composer require topthink/think-migration
3. 数据库设计与模型创建
3.1 创建数据表
以用户表为例,执行以下 SQL:
sql
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(255) NOT NULL COMMENT '密码',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态:1启用 0禁用',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2 配置数据库连接
编辑 .env 文件:
ini
[DATABASE]
HOSTNAME = 127.0.0.1
DATABASE = tp6_api
USERNAME = root
PASSWORD = 123456
HOSTPORT = 3306
CHARSET = utf8mb4
3.3 创建模型
在 app/model 目录下创建 User.php:
php
<?php
declare(strict_types=1);
namespace app\model;
use think\Model;
class User extends Model
{
// 自动写入时间戳
protected $autoWriteTimestamp = true;
// 密码自动加密
public function setPasswordAttr($value)
{
return password_hash($value, PASSWORD_DEFAULT);
}
// 隐藏敏感字段
protected $hidden = ['password'];
}
4. 路由配置
4.1 开启路由
在 config/route.php 中确保路由已开启:
php
'url_route_must' => false,
'route_complete_match' => false,
4.2 定义 API 路由
在 route/app.php 中定义 RESTful 风格路由:
php
<?php
use think\facade\Route;
Route::group('api', function () {
Route::get('users', 'api.User/index');
Route::post('users', 'api.User/save');
Route::get('users/:id', 'api.User/read');
Route::put('users/:id', 'api.User/update');
Route::delete('users/:id', 'api.User/delete');
});
5. 控制器编写
5.1 创建基础控制器
在 app/controller/api 目录下创建 Base.php,统一处理响应格式:
php
<?php
declare(strict_types=1);
namespace app\controller\api;
use think\App;
use think\Response;
class Base
{
protected $request;
public function __construct(App $app)
{
$this->request = $app->request;
}
/**
* 成功响应
*/
protected function success($data = [], string $msg = 'ok'): Response
{
return json([
'code' => 0,
'msg' => $msg,
'data' => $data,
]);
}
/**
* 失败响应
*/
protected function error(string $msg = 'error', int $code = 1, int $httpCode = 200): Response
{
return json([
'code' => $code,
'msg' => $msg,
'data' => [],
], $httpCode);
}
}
5.2 创建用户控制器
在 app/controller/api 目录下创建 User.php:
php
<?php
declare(strict_types=1);
namespace app\controller\api;
use app\model\User as UserModel;
use think\Response;
class User extends Base
{
/**
* 用户列表
*/
public function index(): Response
{
$page = (int) $this->request->param('page', 1);
$pageSize = (int) $this->request->param('page_size', 10);
$list = UserModel::paginate([
'page' => $page,
'list_rows'=> $pageSize,
]);
return $this->success($list);
}
/**
* 用户详情
*/
public function read(int $id): Response
{
$user = UserModel::find($id);
if (!$user) {
return $this->error('用户不存在', 404, 404);
}
return $this->success($user);
}
/**
* 新增用户
*/
public function save(): Response
{
$data = $this->request->post();
// 参数校验
if (empty($data['username']) || empty($data['password'])) {
return $this->error('用户名和密码不能为空');
}
try {
$user = UserModel::create($data);
return $this->success($user, '创建成功');
} catch (\Exception $e) {
return $this->error('创建失败:' . $e->getMessage());
}
}
/**
* 更新用户
*/
public function update(int $id): Response
{
$user = UserModel::find($id);
if (!$user) {
return $this->error('用户不存在', 404, 404);
}
$data = $this->request->put();
if (isset($data['password']) && !empty($data['password'])) {
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
}
$user->save($data);
return $this->success($user, '更新成功');
}
/**
* 删除用户
*/
public function delete(int $id): Response
{
$user = UserModel::find($id);
if (!$user) {
return $this->error('用户不存在', 404, 404);
}
$user->delete();
return $this->success([], '删除成功');
}
}
6. 参数校验与异常处理
6.1 使用验证器
创建 app/validate/User.php:
php
<?php
declare(strict_types=1);
namespace app\validate;
use think\Validate;
class User extends Validate
{
protected $rule = [
'username' => 'require|max:50',
'password' => 'require|min:6',
'email' => 'email',
];
protected $message = [
'username.require' => '用户名不能为空',
'username.max' => '用户名不能超过50个字符',
'password.require' => '密码不能为空',
'password.min' => '密码至少6位',
'email.email' => '邮箱格式不正确',
];
}
在控制器中使用验证器:
php
use app\validate\User as UserValidate;
public function save(): Response
{
$data = $this->request->post();
$validate = new UserValidate();
if (!$validate->check($data)) {
return $this->error($validate->getError());
}
// 继续业务逻辑...
}
6.2 全局异常处理
在 app/ExceptionHandle.php 中统一处理异常:
php
<?php
declare(strict_types=1);
namespace app;
use think\exception\Handle;
use think\Response;
use Throwable;
class ExceptionHandle extends Handle
{
public function render($request, Throwable $e): Response
{
// 参数错误
if ($e instanceof \think\exception\ValidateException) {
return json([
'code' => 422,
'msg' => $e->getError(),
'data' => [],
], 422);
}
// 其他异常
return json([
'code' => 500,
'msg' => $e->getMessage(),
'data' => [],
], 500);
}
}
7. 接口测试
7.1 使用 Postman 测试
启动开发服务器:
bash
php think run
测试接口:
bash
# 获取用户列表
curl http://127.0.0.1:8000/api/users
# 获取用户详情
curl http://127.0.0.1:8000/api/users/1
# 新增用户
curl -X POST http://127.0.0.1:8000/api/users \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"123456","email":"admin@example.com"}'
# 更新用户
curl -X PUT http://127.0.0.1:8000/api/users/1 \
-H "Content-Type: application/json" \
-d '{"email":"new@example.com"}'
# 删除用户
curl -X DELETE http://127.0.0.1:8000/api/users/1
7.2 响应示例
成功响应:
json
{
"code": 0,
"msg": "ok",
"data": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"status": 1,
"create_time": "2026-10-16 21:00:00",
"update_time": "2026-10-16 21:00:00"
}
}
失败响应:
json
{
"code": 404,
"msg": "用户不存在",
"data": []
}
8. 接口安全与优化
8.1 接口限流
使用中间件实现简单的接口限流:
php
<?php
declare(strict_types=1);
namespace app\middleware;
use Closure;
use think\Request;
use think\Response;
class RateLimit
{
public function handle(Request $request, Closure $next): Response
{
$key = 'rate_limit:' . $request->ip();
$cache = cache($key);
if ($cache && $cache >= 100) {
return json([
'code' => 429,
'msg' => '请求过于频繁',
'data' => [],
], 429);
}
cache($key, ($cache ?: 0) + 1, 60);
return $next($request);
}
}
在 app/middleware.php 中注册:
php
return [
'rateLimit' => \app\middleware\RateLimit::class,
];
8.2 跨域处理
创建 Cors 中间件 app/middleware/Cors.php:
php
<?php
declare(strict_types=1);
namespace app\middleware;
use Closure;
use think\Request;
use think\Response;
class Cors
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$response->header([
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers' => 'Content-Type, Authorization',
'Access-Control-Max-Age' => 3600,
]);
return $response;
}
}
8.2.1 在路由中注册 Cors 中间件
在 app/middleware.php 中注册 Cors 中间件:
php
return [
'rateLimit' => \app\middleware\RateLimit::class,
'cors' => \app\middleware\Cors::class,
];
在 route/app.php 中,为整个 api 路由组挂载 Cors 中间件:
php
<?php
use think\facade\Route;
Route::group('api', function () {
// 公开接口
Route::post('login', 'api.Auth/login');
// 受保护接口(需要 Token)
Route::get('profile', 'api.Auth/profile')->middleware(\app\middleware\JwtAuth::class);
Route::get('users', 'api.User/index')->middleware(\app\middleware\JwtAuth::class);
Route::post('users', 'api.User/save')->middleware(\app\middleware\JwtAuth::class);
Route::get('users/:id', 'api.User/read')->middleware(\app\middleware\JwtAuth::class);
Route::put('users/:id', 'api.User/update')->middleware(\app\middleware\JwtAuth::class);
Route::delete('users/:id', 'api.User/delete')->middleware(\app\middleware\JwtAuth::class);
})->middleware(\app\middleware\Cors::class);
说明:
Route::group()返回的路由组对象支持链式调用->middleware(),这样整组路由都会先经过 Cors 中间件处理,无需为每条路由单独挂载。
8.2.2 使用 allowCrossDomain 替代方案
ThinkPHP 6 内置了 allowCrossDomain 跨域快捷方法,无需自定义中间件即可快速开启跨域支持。在 route/app.php 中直接调用:
php
<?php
use think\facade\Route;
// 开启全局跨域支持
Route::allowCrossDomain();
Route::group('api', function () {
Route::post('login', 'api.Auth/login');
Route::get('profile', 'api.Auth/profile')->middleware(\app\middleware\JwtAuth::class);
// ... 其他路由
});
allowCrossDomain() 默认允许所有来源跨域访问,并自动处理 OPTIONS 预检请求。如果需要自定义允许的域名或请求头,可以传入参数:
php
Route::allowCrossDomain([
'Access-Control-Allow-Origin' => 'https://example.com',
'Access-Control-Allow-Headers' => 'Content-Type, Authorization',
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE',
]);
两种方式的对比:
| 方案 | 优点 | 适用场景 |
|---|---|---|
自定义 Cors 中间件 |
灵活可控,可自定义响应头、按路由精细控制 | 需要区分不同域名、不同接口策略时 |
allowCrossDomain() |
配置简单,一行开启,内置预检处理 | 快速开发、前后端分离的通用场景 |
8.2.3 跨域中间件与 JWT 认证中间件的执行顺序
在 ThinkPHP 6 中,中间件按「先注册先执行」的顺序依次调用。当路由组同时挂载 Cors 与 JWT 认证时,执行顺序如下:
php
Route::group('api', function () {
Route::get('profile', 'api.Auth/profile')->middleware(\app\middleware\JwtAuth::class);
})->middleware(\app\middleware\Cors::class);
执行流程:
#mermaid-svg-BldcxXjBZ5sHC6kd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BldcxXjBZ5sHC6kd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BldcxXjBZ5sHC6kd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BldcxXjBZ5sHC6kd .error-icon{fill:#552222;}#mermaid-svg-BldcxXjBZ5sHC6kd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BldcxXjBZ5sHC6kd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BldcxXjBZ5sHC6kd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BldcxXjBZ5sHC6kd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BldcxXjBZ5sHC6kd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BldcxXjBZ5sHC6kd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BldcxXjBZ5sHC6kd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BldcxXjBZ5sHC6kd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BldcxXjBZ5sHC6kd .marker.cross{stroke:#333333;}#mermaid-svg-BldcxXjBZ5sHC6kd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BldcxXjBZ5sHC6kd p{margin:0;}#mermaid-svg-BldcxXjBZ5sHC6kd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BldcxXjBZ5sHC6kd .cluster-label text{fill:#333;}#mermaid-svg-BldcxXjBZ5sHC6kd .cluster-label span{color:#333;}#mermaid-svg-BldcxXjBZ5sHC6kd .cluster-label span p{background-color:transparent;}#mermaid-svg-BldcxXjBZ5sHC6kd .label text,#mermaid-svg-BldcxXjBZ5sHC6kd span{fill:#333;color:#333;}#mermaid-svg-BldcxXjBZ5sHC6kd .node rect,#mermaid-svg-BldcxXjBZ5sHC6kd .node circle,#mermaid-svg-BldcxXjBZ5sHC6kd .node ellipse,#mermaid-svg-BldcxXjBZ5sHC6kd .node polygon,#mermaid-svg-BldcxXjBZ5sHC6kd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BldcxXjBZ5sHC6kd .rough-node .label text,#mermaid-svg-BldcxXjBZ5sHC6kd .node .label text,#mermaid-svg-BldcxXjBZ5sHC6kd .image-shape .label,#mermaid-svg-BldcxXjBZ5sHC6kd .icon-shape .label{text-anchor:middle;}#mermaid-svg-BldcxXjBZ5sHC6kd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BldcxXjBZ5sHC6kd .rough-node .label,#mermaid-svg-BldcxXjBZ5sHC6kd .node .label,#mermaid-svg-BldcxXjBZ5sHC6kd .image-shape .label,#mermaid-svg-BldcxXjBZ5sHC6kd .icon-shape .label{text-align:center;}#mermaid-svg-BldcxXjBZ5sHC6kd .node.clickable{cursor:pointer;}#mermaid-svg-BldcxXjBZ5sHC6kd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BldcxXjBZ5sHC6kd .arrowheadPath{fill:#333333;}#mermaid-svg-BldcxXjBZ5sHC6kd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BldcxXjBZ5sHC6kd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BldcxXjBZ5sHC6kd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BldcxXjBZ5sHC6kd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BldcxXjBZ5sHC6kd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BldcxXjBZ5sHC6kd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BldcxXjBZ5sHC6kd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BldcxXjBZ5sHC6kd .cluster text{fill:#333;}#mermaid-svg-BldcxXjBZ5sHC6kd .cluster span{color:#333;}#mermaid-svg-BldcxXjBZ5sHC6kd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BldcxXjBZ5sHC6kd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BldcxXjBZ5sHC6kd rect.text{fill:none;stroke-width:0;}#mermaid-svg-BldcxXjBZ5sHC6kd .icon-shape,#mermaid-svg-BldcxXjBZ5sHC6kd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BldcxXjBZ5sHC6kd .icon-shape p,#mermaid-svg-BldcxXjBZ5sHC6kd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BldcxXjBZ5sHC6kd .icon-shape .label rect,#mermaid-svg-BldcxXjBZ5sHC6kd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BldcxXjBZ5sHC6kd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BldcxXjBZ5sHC6kd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BldcxXjBZ5sHC6kd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
Token 有效
Token 无效
浏览器发起请求
Cors 中间件
设置跨域响应头
是否为 OPTIONS 预检请求
直接返回跨域响应头
不进入业务逻辑
JwtAuth 中间件
校验 Token
控制器处理业务
返回 401 未授权
关键点说明:
- Cors 先执行 :路由组级中间件(
->middleware())先于路由级中间件执行,因此 Cors 会先设置跨域响应头。这样即使后续 JWT 认证返回 401,浏览器也能正确读取到跨域响应头,避免前端因跨域拦截而无法看到错误信息。 - 预检请求直接放行 :浏览器跨域请求前会先发送
OPTIONS预检请求。Cors 中间件应识别OPTIONS请求并直接返回,不进入 JWT 认证校验,否则预检请求会因缺少 Token 而返回 401,导致真正的请求无法发出。 - JWT 认证后执行:在跨域响应头设置完成后,JWT 认证再校验 Token,保证受保护接口的身份验证在跨域处理之后进行。
如需在 Cors 中间件中直接放行预检请求,可改造如下:
php
<?php
declare(strict_types=1);
namespace app\middleware;
use Closure;
use think\Request;
use think\Response;
class Cors
{
public function handle(Request $request, Closure $next): Response
{
// 预检请求直接返回,不进入后续中间件
if ($request->method() === 'OPTIONS') {
return json([], 204)->header([
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers' => 'Content-Type, Authorization',
'Access-Control-Max-Age' => 3600,
]);
}
$response = $next($request);
$response->header([
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers' => 'Content-Type, Authorization',
'Access-Control-Max-Age' => 3600,
]);
return $response;
}
}
8.3 JWT 认证
JWT(JSON Web Token)是一种轻量级的身份认证方案,适合前后端分离的 API 场景。下面演示如何在 ThinkPHP 6 中集成 JWT 认证。
8.3.1 安装扩展
使用 Composer 安装 firebase/php-jwt:
bash
composer require firebase/php-jwt
8.3.2 创建 Token 服务类
在 app/service 目录下创建 JwtService.php,封装 Token 的生成与解析:
php
<?php
declare(strict_types=1);
namespace app\service;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class JwtService
{
// 密钥,生产环境建议从 .env 读取
private const SECRET = 'your-secret-key-change-me';
// 过期时间(秒),默认 2 小时
private const EXPIRE = 7200;
/**
* 生成 Token
*/
public function createToken(int $userId, string $username): string
{
$payload = [
'iss' => 'ThinkPHP 6 API', // 签发者
'iat' => time(), // 签发时间
'exp' => time() + self::EXPIRE, // 过期时间
'uid' => $userId, // 用户 ID
'name' => $username, // 用户名
];
return JWT::encode($payload, self::SECRET, 'HS256');
}
/**
* 解析 Token,成功返回 payload,失败抛出异常
*/
public function parseToken(string $token): array
{
$decoded = JWT::decode($token, new Key(self::SECRET, 'HS256'));
return (array) $decoded;
}
}
8.3.3 创建 Token 验证中间件
在 app/middleware 目录下创建 JwtAuth.php:
php
<?php
declare(strict_types=1);
namespace app\middleware;
use Closure;
use think\Request;
use think\Response;
use app\service\JwtService;
class JwtAuth
{
public function handle(Request $request, Closure $next): Response
{
// 从 Authorization 请求头获取 Token
$authorization = $request->header('authorization', '');
$token = '';
if (preg_match('/Bearer\s+(.*)$/i', $authorization, $matches)) {
$token = $matches[1];
}
if (empty($token)) {
return json([
'code' => 401,
'msg' => '未登录或 Token 缺失',
'data' => [],
], 401);
}
try {
$jwt = new JwtService();
$payload = $jwt->parseToken($token);
// 将用户信息绑定到请求,供后续控制器使用
$request->user = [
'uid' => $payload['uid'],
'name' => $payload['name'],
];
return $next($request);
} catch (\Exception $e) {
return json([
'code' => 401,
'msg' => 'Token 无效或已过期',
'data' => [],
], 401);
}
}
}
在 app/middleware.php 中注册 Cors 中间件:
php
return [
'rateLimit' => \app\middleware\RateLimit::class,
'jwtAuth' => \app\middleware\JwtAuth::class,
];
8.3.4 登录接口返回 Token
在 app/controller/api 目录下创建 Auth.php:
php
<?php
declare(strict_types=1);
namespace app\controller\api;
use app\model\User as UserModel;
use app\service\JwtService;
use think\Response;
class Auth extends Base
{
/**
* 用户登录
*/
public function login(): Response
{
$username = $this->request->post('username', '');
$password = $this->request->post('password', '');
if (empty($username) || empty($password)) {
return $this->error('用户名和密码不能为空');
}
$user = UserModel::where('username', $username)->find();
if (!$user || !password_verify($password, $user->password)) {
return $this->error('用户名或密码错误', 401, 401);
}
// 生成 Token
$jwt = new JwtService();
$token = $jwt->createToken((int) $user->id, $user->username);
return $this->success([
'token' => $token,
'user' => [
'id' => $user->id,
'username' => $user->username,
'email' => $user->email,
],
], '登录成功');
}
/**
* 获取当前登录用户信息(受保护接口示例)
*/
public function profile(): Response
{
$user = $this->request->user;
return $this->success($user, '获取成功');
}
}
在 route/app.php 中新增路由:
php
<?php
use think\facade\Route;
Route::group('api', function () {
// 公开接口
Route::post('login', 'api.Auth/login');
// 受保护接口(需要 Token)
Route::get('profile', 'api.Auth/profile')->middleware(\app\middleware\JwtAuth::class);
Route::get('users', 'api.User/index')->middleware(\app\middleware\JwtAuth::class);
Route::post('users', 'api.User/save')->middleware(\app\middleware\JwtAuth::class);
Route::get('users/:id', 'api.User/read')->middleware(\app\middleware\JwtAuth::class);
Route::put('users/:id', 'api.User/update')->middleware(\app\middleware\JwtAuth::class);
Route::delete('users/:id', 'api.User/delete')->middleware(\app\middleware\JwtAuth::class);
});
8.3.5 测试 JWT 认证
登录获取 Token:
bash
curl -X POST http://127.0.0.1:8000/api/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"123456"}'
登录成功响应:
json
{
"code": 0,
"msg": "登录成功",
"data": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0cDYtYXBpIiwiaWF0IjoxNzI5MDAwMDAwLCJleHAiOjE3MjkwMDcyMDAsInVpZCI6MSwibmFtZSI6ImFkbWluIn0.signature",
"user": {
"id": 1,
"username": "admin",
"email": "admin@example.com"
}
}
}
携带 Token 访问受保护接口:
bash
curl http://127.0.0.1:8000/api/profile \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0cDYtYXBpIiwiaWF0IjoxNzI5MDAwMDAwLCJleHAiOjE3MjkwMDcyMDAsInVpZCI6MSwibmFtZSI6ImFkbWluIn0.signature"
未携带或 Token 无效时返回:
json
{
"code": 401,
"msg": "未登录或 Token 缺失",
"data": []
}
创建 Cors 中间件 app/middleware/Cors.php:
php
<?php
declare(strict_types=1);
namespace app\middleware;
use Closure;
use think\Request;
use think\Response;
class Cors
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$response->header([
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers' => 'Content-Type, Authorization',
'Access-Control-Max-Age' => 3600,
]);
return $response;
}
}
9. 总结
本文通过一个完整的用户管理模块,演示了 ThinkPHP 6 API 接口开发的完整流程:
- 使用 Composer 创建项目并配置数据库
- 通过模型层封装数据操作
- 使用 RESTful 风格路由定义接口
- 编写控制器实现增删改查
- 使用验证器进行参数校验
- 统一异常处理和响应格式
- 通过中间件实现限流和跨域
在实际项目中,还可以进一步引入 JWT 认证、接口文档生成(如 Swagger/OpenAPI)、单元测试等机制,构建更健壮的 API 服务。希望本文能帮助你快速上手 ThinkPHP 6 的 API 开发。