前言随着前后端分离架构的普及，FastAdmin 的 API 模块越来越重要。本文将系统讲解如何使用 FastAdmin 开发 RESTful API 接口，包括 Token 认证、参数验证、数据返回格式规范以及与前端框架的对接实践。一、API 模块概述FastAdmin 的 API 模块位于 application/api/，专门用于提供接口服务：application/api/ ├── controller/ │ ├── Common.php # 公共控制器 │ ├── Index.php # 首页接口 │ ├── User.php # 用户接口 │ ├── Api.php # 基类控制器 │ └── Demo.php # 示例接口 ├── model/ │ └── User.php # API 用户模型 └── config.php # API 模块配置二、API 基类控制器所有 API 控制器继承自 app\common\controller\Api：<?php // application/common/controller/Api.php namespace app\common\controller; use think\Controller; use app\common\library\Token; use app\common\library\Auth; class Api extends Controller { // 不需要登录的方法 protected $noNeedLogin = ['*']; // 不需要权限验证的方法 protected $noNeedRight = ['*']; // 当前请求的 Token protected $token = ''; // 当前登录用户 protected $auth = null; // 初始化 public function _initialize() { parent::_initialize(); // 跨域处理 $this->checkCors(); // Token 认证 $this->token = $this->request->server('HTTP_TOKEN', $this->request->request('token', '')); // 初始化认证 $this->auth = Auth::instance(); // 检查是否需要登录 $action = strtolower($this->request->action()); if (!in_array($action, array_map('strtolower', $this->noNeedLogin))) { if (!$this->auth->isLogin()) { $this->error(__('Please login first'), null, 401); } } } // 跨域设置 protected function checkCors() { header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS'); header('Access-Control-Allow-Headers: token, Content-Type, Authorization'); if ($this->request->method() == 'OPTIONS') { $this->success(); } } // 成功返回 protected function success($msg = '', $data = null, $code = 1) { $this->result($msg, $data, $code); } // 失败返回 protected function error($msg = '', $data = null, $code = 0) { $this->result($msg, $data, $code); } // 统一返回格式 protected function result($msg, $data = null, $code = 1) { $result = [ 'code' => $code, 'msg' => $msg, 'time' => time(), 'data' => $data, ]; throw new \think\exception\HttpResponseException( json($result) ); } }统一返回格式{ "code": 1, "msg": "获取成功", "time": 1721800000, "data": { "id": 1, "username": "admin" } }字段说明code1=成功，0=失败msg提示消息time服务器时间戳data业务数据三、用户认证3.1 注册接口<?php // application/api/controller/User.php namespace app\api\controller; use app\common\controller\Api; use app\common\model\User; use app\common\library\Token; use think\Validate; class User extends Api { protected $noNeedLogin = ['register', 'login']; protected $noNeedRight = ['*']; /** * 用户注册 * POST /api/user/register */ public function register() { $username = $this->request->post('username'); $password = $this->request->post('password'); $email = $this->request->post('email'); $mobile = $this->request->post('mobile'); // 参数验证 $validate = new Validate([ 'username' => 'require|length:3,30', 'password' => 'require|length:6,30', 'email' => 'require|email', 'mobile' => 'require|regex:/^1[3-9]\d{9}$/', ], [ 'username.require' => '用户名不能为空', 'username.length' => '用户名长度3-30个字符', 'password.require' => '密码不能为空', 'password.length' => '密码长度6-30个字符', 'email.email' => '邮箱格式不正确', 'mobile.regex' => '手机号格式不正确', ]); $data = [ 'username' => $username, 'password' => $password, 'email' => $email, 'mobile' => $mobile, ]; if (!$validate->check($data)) { $this->error($validate->getError()); } // 检查用户名是否已存在 if (User::where('username', $username)->find()) { $this->error('用户名已存在'); } // 创建用户 $user = new User(); $user->allowField(true)->save([ 'username' => $username, 'password' => \app\common\library\Hash::make($password), 'email' => $email, 'mobile' => $mobile, 'jointime' => time(), 'createtime' => time(), 'updatetime' => time(), ]); // 生成 Token $token = Token::create($user->id, 'user', 86400 * 7); $this->success('注册成功', [ 'token' => $token, 'user' => [ 'id' => $user->id, 'username' => $user->username, 'email' => $user->email, 'mobile' => $user->mobile, ], ]); } /** * 用户登录 * POST /api/user/login */ public function login() { $account = $this->request->post('account'); $password = $this->request->post('password'); if (!$account || !$password) { $this->error('账号和密码不能为空'); } // 支持用户名/邮箱/手机号登录 $user = User::where('username|email|mobile', $account)->find(); if (!$user) { $this->error('账号不存在'); } if (!\app\common\library\Hash::check($password, $user->password)) { $this->error('密码错误'); } if ($user->status != 'normal') { $this->error('账号已被禁用'); } // 更新登录信息 $user->save([ 'logintime' => time(), 'loginip' => $this->request->ip(), ]); // 生成 Token $token = Token::create($user->id, 'user', 86400 * 7); $this->success('登录成功', [ 'token' => $token, 'user' => [ 'id' => $user->id, 'username' => $user->username, 'avatar' => $user->avatar, 'email' => $user->email, ], ]); } /** * 获取当前用户信息 * GET /api/user/info */ public function info() { $user = $this->auth->getUser(); $this->success('获取成功', [ 'id' => $user->id, 'username' => $user->username, 'avatar' => $user->avatar, 'email' => $user->email, 'mobile' => $user->mobile, 'createtime'=> $user->createtime, ]); } /** * 退出登录 * POST /api/user/logout */ public function logout() { Token::clear($this->token); $this->success('退出成功'); } }3.2 Token 机制FastAdmin 的 Token 存储在 fa_token 表中：// 生成 Token（有效期 7 天） $token = Token::create($userId, 'user', 86400 * 7); // 验证 Token $userId = Token::get($token); // 刷新 Token $token = Token::refresh($token); // 清除 Token Token::clear($token);前端请求时在 Header 中携带 Token：// axios 请求拦截器 axios.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers['token'] = token; } return config; }); // 响应拦截器处理 401 axios.interceptors.response.use( response => response.data, error => { if (error.response && error.response.status === 401) { localStorage.removeItem('token'); router.push('/login'); } return Promise.reject(error); } );四、RESTful 接口开发4.1 文章接口完整实现<?php // application/api/controller/Article.php namespace app\api\controller; use app\common\controller\Api; use app\common\model\Article as ArticleModel; use think\Validate; class Article extends Api { protected $noNeedLogin = ['index', 'detail']; protected $noNeedRight = ['*']; /** * 文章列表 * GET /api/article/index?page=1&limit=10&keyword=xxx */ public function index() { $page = $this->request->get('page/d', 1); $limit = $this->request->get('limit/d', 10); $keyword = $this->request->get('keyword', ''); $categoryId = $this->request->get('category_id/d', 0); $query = ArticleModel::where('status', 'normal'); // 关键词搜索 if ($keyword) { $query->where('title', 'like', "%{$keyword}%"); } // 分类筛选 if ($categoryId) { $query->where('category_id', $categoryId); } // 分页查询 $total = $query->count(); $list = $query->order('id', 'desc') ->field('id,title,author,views,createtime') ->page($page, $limit) ->select(); $this->success('获取成功', [ 'total' => $total, 'page' => $page, 'limit' => $limit, 'list' => $list, ]); } /** * 文章详情 * GET /api/article/detail?id=1 */ public function detail() { $id = $this->request->get('id/d', 0); if (!$id) { $this->error('参数错误'); } $article = ArticleModel::where('id', $id) ->where('status', 'normal') ->find(); if (!$article) { $this->error('文章不存在'); } // 浏览量 +1 ArticleModel::where('id', $id)->inc('views')->update(); $this->success('获取成功', $article); } /** * 创建文章 * POST /api/article/add */ public function add() { $data = $this->request->post(); $data['admin_id'] = $this->auth->id; $validate = new Validate([ 'title' => 'require|length:2,100', 'content' => 'require|min:10', ], [ 'title.require' => '标题不能为空', 'content.require' => '内容不能为空', ]); if (!$validate->check($data)) { $this->error($validate->getError()); } $data['createtime'] = time(); $data['updatetime'] = time(); $article = new ArticleModel(); $article->allowField(true)->save($data); $this->success('创建成功', ['id' => $article->id]); } /** * 编辑文章 * PUT /api/article/edit?id=1 */ public function edit() { $id = $this->request->get('id/d', 0); $data = $this->request->post(); $article = ArticleModel::find($id); if (!$article) { $this->error('文章不存在'); } // 权限检查：只能编辑自己的文章 if ($article->admin_id != $this->auth->id) { $this->error('无权操作', null, 403); } $data['updatetime'] = time(); $article->allowField(true)->save($data); $this->success('修改成功'); } /** * 删除文章 * DELETE /api/article/del?id=1 */ public function del() { $id = $this->request->get('id/d', 0); $article = ArticleModel::find($id); if (!$article) { $this->error('文章不存在'); } if ($article->admin_id != $this->auth->id) { $this->error('无权操作', null, 403); } $article->delete(); $this->success('删除成功'); } }4.2 路由配置在 application/api/route.php 中定义路由规则：<?php use think\Route; // 文章相关 Route::get('articles', 'api/article/index'); Route::get('article/:id', 'api/article/detail'); Route::post('article', 'api/article/add'); Route::put('article/:id', 'api/article/edit'); Route::delete('article/:id', 'api/article/del'); // 用户相关 Route::post('register', 'api/user/register'); Route::post('login', 'api/user/login'); Route::get('user/info', 'api/user/info'); Route::post('logout', 'api/user/logout');五、接口文档自动生成FastAdmin 内置 API 文档生成功能：# 生成 API 文档 php think api --force=true文档输出到 public/api.html，包含所有接口的参数说明、返回示例和在线测试功能。注释规范/** * @ApiTitle (文章列表) * @ApiSummary (获取文章列表，支持分页和搜索) * @ApiMethod (GET) * @ApiRoute (/api/article/index) * @ApiParams (name="page", type="integer", required=false, description="页码，默认1") * @ApiParams (name="limit", type="integer", required=false, description="每页条数，默认10") * @ApiParams (name="keyword", type="string", required=false, description="搜索关键词") * @ApiReturn ({"code":1,"msg":"获取成功","data":{"total":100,"list":[...]}}) */ public function index() { // ... }六、与前端对接6.1 Vue3 对接示例// api/article.ts import axios from 'axios' const API_BASE = '/api' export interface Article { id: number title: string author: string views: number createtime: number } export interface ArticleListResponse { total: number page: number limit: number list: Article[] } export const getArticles = (params: { page?: number limit?: number keyword?: string }) => { return axios.get<{ code: number; data: ArticleListResponse }>( `${API_BASE}/article/index`, { params } ) } export const getArticleDetail = (id: number) => { return axios.get<{ code: number; data: Article }>( `${API_BASE}/article/detail`, { params: { id } } ) }6.2 小程序对接示例// utils/request.js const BASE_URL = 'https://api.example.com' export function request(options) { const token = wx.getStorageSync('token') return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + options.url, method: options.method || 'GET', data: options.data, header: { 'token': token, 'Content-Type': 'application/json' }, success(res) { if (res.data.code === 1) { resolve(res.data.data) } else if (res.data.code === 0 && res.statusCode === 401) { wx.removeStorageSync('token') wx.redirectTo({ url: '/pages/login/login' }) } else { wx.showToast({ title: res.data.msg, icon: 'none' }) reject(res.data) } }, fail(err) { reject(err) } }) }) } // 调用 request({ url: '/api/article/index', data: { page: 1, limit: 10 } }) .then(data => { console.log(data.list) })七、安全防护7.1 接口限流// 使用缓存实现简单限流 public function _initialize() { parent::_initialize(); $ip = $this->request->ip(); $key = 'api_rate_' . $ip; $count = \think\Cache::inc($key); if ($count == 1) { \think\Cache::expire($key, 60); // 60秒内 } if ($count > 60) { $this->error('请求过于频繁', null, 429); } }7.2 签名验证// 客户端生成签名 // sign = md5(参数按key排序拼接 + secret_key) public function verifySign($params, $sign) { unset($params['sign']); ksort($params); $str = http_build_query($params) . '&key=' . config('api_secret'); return md5($str) === $sign; }总结FastAdmin API 模块提供了完整的前后端分离方案：统一返回格式和错误处理Token 认证机制，支持多端登录RESTful 路由设计，接口语义清晰内置 API 文档自动生成配合 Vue3/小程序等前端框架，快速搭建完整应用