ThinkPHP6 API接口开发实例:从零搭建RESTful风格接口

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 开发。

相关推荐
开心猴爷1 小时前
应对iOS审核4.3:可以使用 IpaGuard 来给属性名混淆的技术细节与平衡之道
后端·ios
1m555555551 小时前
RAG 工程实战(五):正确知识已经进入 Prompt,为什么大模型还是会答错?
后端·面试
易小尊1 小时前
我的模型在 65% 以上时准得可怕,在 50% 时像在掷硬币
后端
用户2788607285671 小时前
fallback 会盖掉平台提示词:system、空数组和 systemAppend 的差别
前端·后端
iOS开发上架哦1 小时前
iOS应用耗电测试:使用Energy Log、Keymob和Instruments进行性能监控与能源消耗分析
后端·ios
掘金挖土1 小时前
前端手摸手跑路之 AI 应用开发(十一)
前端·后端
用户9888321871741 小时前
从“限流数字”到可维护策略:ASP.NET Core 固定窗口限流的落地边界
后端
IT_陈寒1 小时前
Vue动态组件缓存的坑你踩过了吗?
前端·人工智能·后端
雾行0x002 小时前
粗排:如何在毫秒级筛出值得精排的内容?
后端·架构