PHP RESTful API 设计规范
基础规范
1 协议与域名
- 使用 HTTPS 协议
- API 使用专用域名:
api.example.com - 或保留在主域名下:
example.com/api
2 版本控制
// URL 路径版本控制(推荐) /API/v1/users /API/v2/users // 请求头版本控制 Accept: application/vnd.example.v1+json
路由设计
1 URL 命名规范
- 使用名词复数形式
- 不使用动词
- 使用小写字母
- 多个单词使用短横线(-)分隔
// ✅ 正确的设计 GET /api/v1/users // 用户列表 GET /api/v1/users/123 // 指定用户 POST /api/v1/users // 创建用户 PUT /api/v1/users/123 // 更新用户 DELETE /api/v1/users/123 // 删除用户 // ❌ 避免的设计 GET /api/v1/getUsers // 使用动词 GET /api/v1/UserList // 混合大小写 POST /api/v1/getUserData // 动词+连接词
2 资源嵌套
// 多级资源 GET /api/v1/users/123/orders/456 // 用户123的订单456 GET /api/v1/users/123/orders // 用户123的所有订单 // 子资源关联 GET /api/v1/users/123/profile // 用户资料 GET /api/v1/users/123/posts // 用户的文章
HTTP 方法
1 方法使用规范
// 标准操作 GET - 获取资源(幂等) POST - 创建资源(非幂等) PUT - 完整更新(幂等) PATCH - 部分更新(幂等) DELETE - 删除资源(幂等) // 实际操作示例 GET /api/v1/users // 获取用户列表 POST /api/v1/users // 创建用户 GET /api/v1/users/123 // 获取指定用户 PUT /api/v1/users/123 // 完整更新用户 PATCH /api/v1/users/123 // 部分更新用户 DELETE /api/v1/users/123 // 删除用户
请求设计
1 请求头规范
header('Content-Type: application/json; charset=utf-8');
header('Accept: application/json');
header('Authorization: Bearer {token}');
header('X-App-Version: 1.0.0'); // 客户端版本
header('X-Device-Id: unique-device-id'); // 设备标识
2 查询参数
// 筛选 GET /api/v1/users?status=active&age_group=20-30 // 排序 GET /api/v1/users?sort=-created_at // 降序 GET /api/v1/users?sort=created_at // 升序 // 分页 GET /api/v1/users?page=1&limit=20 // 字段选择 GET /api/v1/users?fields=id,name,email
响应设计
1 统一响应格式
// 成功响应
{
"code": 200,
"message": "success",
"data": {
"id": 1,
"name": "张三",
"email": "zhangsan@example.com"
},
"meta": {
"timestamp": 1634567890,
"copyright": "2024 Company"
}
}
// 列表响应
{
"code": 200,
"message": "success",
"data": [
{
"id": 1,
"name": "张三"
},
{
"id": 2,
"name": "李四"
}
],
"pagination": {
"total": 100,
"page": 1,
"limit": 20,
"pages": 5
}
}
2 错误响应
// 4xx 客户端错误
{
"code": 400,
"message": "请求参数错误",
"errors": {
"email": ["邮箱格式不正确"],
"password": ["密码长度不少于6位"]
}
}
// 5xx 服务器错误
{
"code": 500,
"message": "服务器内部错误",
"request_id": "a1b2c3d4-e5f6-g7h8"
}
PHP 实现示例
1 路由配置
// config/routes.php
<?php
use App\Controllers\UserController;
$routes = [
// 用户路由
'GET' => [
'/api/v1/users' => [UserController::class, 'index'],
'/api/v1/users/{id}' => [UserController::class, 'show'],
'/api/v1/users/{id}/orders' => [OrderController::class, 'getUserOrders'],
],
'POST' => [
'/api/v1/users' => [UserController::class, 'store'],
'/api/v1/users/login' => [AuthController::class, 'login'],
],
'PUT' => [
'/api/v1/users/{id}' => [UserController::class, 'update'],
],
'PATCH' => [
'/api/v1/users/{id}' => [UserController::class, 'partialUpdate'],
],
'DELETE' => [
'/api/v1/users/{id}' => [UserController::class, 'destroy'],
],
];
2 控制器基类
// app/Controllers/BaseController.php
<?php
class BaseController
{
protected function success($data = null, $code = 200, $message = 'success')
{
http_response_code($code);
header('Content-Type: application/json; charset=utf-8');
$response = [
'code' => $code,
'message' => $message,
'data' => $data,
'meta' => [
'timestamp' => time()
]
];
echo json_encode($response);
exit;
}
protected function error($message, $code = 400, $errors = null)
{
http_response_code($code);
header('Content-Type: application/json; charset=utf-8');
$response = [
'code' => $code,
'message' => $message,
'errors' => $errors
];
echo json_encode($response);
exit;
}
protected function paginate($data, $page, $limit, $total)
{
http_response_code(200);
header('Content-Type: application/json; charset=utf-8');
$response = [
'code' => 200,
'message' => 'success',
'data' => $data,
'pagination' => [
'total' => $total,
'page' => $page,
'limit' => $limit,
'pages' => ceil($total / $limit)
]
];
echo json_encode($response);
exit;
}
protected function validate($data, $rules)
{
// 实现验证逻辑
$errors = [];
foreach ($rules as $field => $rule) {
if (!isset($data[$field]) || !preg_match($rule, $data[$field])) {
$errors[$field] = "字段:{$field} 验证失败";
}
}
if (!empty($errors)) {
$this->error('验证失败', 422, $errors);
}
return true;
}
protected function getRequestData()
{
// 获取JSON请求体
$json = file_get_contents('php://input');
$data = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$this->error('无效的JSON格式', 400);
}
return $data ?: [];
}
protected function getAuthUser()
{
// 从请求头获取Authorization
$headers = getallheaders();
$auth = $headers['Authorization'] ?? '';
if (preg_match('/Bearer\s+(.*)/', $auth, $matches)) {
// 解析token并返回用户
return JWT::decode($matches[1]);
}
return null;
}
}
3 用户控制器
// app/Controllers/UserController.php
<?php
class UserController extends BaseController
{
private $userModel;
public function __construct()
{
$this->userModel = new UserModel();
}
// 获取用户列表
public function index(Request $request)
{
// 参数校验
$page = $request->query('page', 1);
$limit = $request->query('limit', 20);
$limit = min($limit, 100); // 限制最大条数
// 构建查询条件
$conditions = [];
if ($request->query('status')) {
$conditions[] = "status = '{$request->query('status')}'";
}
if ($request->query('fields')) {
$fields = explode(',', $request->query('fields'));
$select = implode(',', $fields);
} else {
$select = '*';
}
// 排序
$sort = $request->query('sort', '-created_at');
$sortDirection = str_starts_with($sort, '-') ? 'DESC' : 'ASC';
$sortField = ltrim($sort, '-');
// 查询数据
$users = $this->userModel->getUsers(
$conditions,
$sortField,
$sortDirection,
$page,
$limit,
$select
);
$total = $this->userModel->countUsers($conditions);
return $this->paginate($users, $page, $limit, $total);
}
// 获取指定用户
public function show($id)
{
// 参数验证
if (!is_numeric($id)) {
return $this->error('无效的用户ID', 400);
}
$user = $this->userModel->find($id);
if (!$user) {
return $this->error('用户不存在', 404);
}
// 去除敏感字段(如密码)
unset($user['password']);
return $this->success($user);
}
// 创建用户
public function store()
{
$data = $this->getRequestData();
// 数据验证
$rules = [
'name' => '/^[\x{4e00}-\x{9fa5}a-zA-Z0-9]{2,30}$/u',
'email' => '/^[\w\.-]+@[\w\.-]+\.\w+$/',
'password' => '/^.{6,32}$/'
];
$this->validate($data, $rules);
// 检查邮箱唯一性
if ($this->userModel->findByEmail($data['email'])) {
return $this->error('邮箱已被注册', 409);
}
// 密码加密
$data['password'] = password_hash($data['password'], PASSWORD_BCRYPT);
// 创建用户
$userId = $this->userModel->create($data);
if (!$userId) {
return $this->error('用户创建失败', 500);
}
// 返回创建成功的用户
$user = $this->userModel->find($userId);
unset($user['password']);
return $this->success($user, 201, '用户创建成功');
}
// 完整更新用户
public function update($id)
{
$user = $this->userModel->find($id);
if (!$user) {
return $this->error('用户不存在', 404);
}
$data = $this->getRequestData();
// 验证必需字段
$rules = [
'name' => '/^[\x{4e00}-\x{9fa5}a-zA-Z0-9]{2,30}$/u',
'email' => '/^[\w\.-]+@[\w\.-]+\.\w+$/'
];
$this->validate($data, $rules);
// 更新用户
$result = $this->userModel->update($id, $data);
if (!$result) {
return $this->error('用户更新失败', 500);
}
$updatedUser = $this->userModel->find($id);
unset($updatedUser['password']);
return $this->success($updatedUser);
}
// 删除用户
public function destroy($id)
{
$user = $this->userModel->find($id);
if (!$user) {
return $this->error('用户不存在', 404);
}
$result = $this->userModel->delete($id);
if (!$result) {
return $this->error('用户删除失败', 500);
}
return $this->success(null, 204, '用户已删除');
}
}
安全规范
1 认证与授权
// JWT实现基础认证
class AuthController extends BaseController
{
public function login()
{
$data = $this->getRequestData();
// 验证用户名密码
$user = $this->userModel->findByEmail($data['email']);
if (!$user || !password_verify($data['password'], $user['password'])) {
return $this->error('邮箱或密码错误', 401);
}
// 生成JWT Token
$payload = [
'user_id' => $user['id'],
'exp' => time() + 7200 // 2小时过期
];
$token = JWT::encode($payload, getenv('JWT_SECRET'), 'HS256');
return $this->success([
'token' => $token,
'token_type' => 'Bearer',
'expires_in' => 7200
]);
}
}
2 限流
class RateLimiter
{
public static function check($userId = null, $limit = 100, $window = 60)
{
$key = 'rate_limit:' . ($userId ?: getClientIp());
// 使用Redis实现
$current = Redis::get($key);
if ($current === false) {
Redis::setex($key, $window, 1);
} elseif ($current >= $limit) {
throw new ApiException('请求过于频繁', 429);
} else {
Redis::incr($key);
}
}
}
最佳实践
1 缓存策略
// HTTP缓存头
header('Cache-Control: private, max-age=300');
header('ETag: "' . md5(json_encode($data)) . '"');
// 条件请求
if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$etag = trim($_SERVER['HTTP_IF_NONE_MATCH'], '"');
if ($etag == md5(json_encode($data))) {
http_response_code(304);
exit;
}
}
2 日志记录
class ApiLogger
{
public static function log($request, $response, $duration)
{
$logData = [
'request_time' => date('Y-m-d H:i:s'),
'duration' => $duration,
'ip' => getClientIp(),
'method' => $request['REQUEST_METHOD'],
'uri' => $request['REQUEST_URI'],
'params' => json_encode($request['QUERY_STRING']),
'response_code' => $response['code'],
'launch_client' => $request['HTTP_USER_AGENT'] ?? ''
];
// 写入日志文件
file_put_contents(
LOG_PATH . '/api_' . date('Y-m-d') . '.log',
json_encode($logData) . PHP_EOL,
FILE_APPEND
);
}
}
3 异常处理
// 全局异常处理
class ApiException extends Exception
{
private $errors;
public function __construct($message, $code = 400, $errors = null)
{
parent::__construct($message, $code);
$this->errors = $errors;
}
public function getErrors()
{
return $this->errors;
}
}
// 自定义错误处理函数
set_exception_handler(function ($exception) {
if ($exception instanceof ApiException) {
$response = [
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
'errors' => $exception->getErrors()
];
} else {
$response = [
'code' => 500,
'message' => '服务器内部错误',
'request_id' => uniqid()
];
}
http_response_code($response['code']);
header('Content-Type: application/json; charset=utf-8');
echo json_encode($response);
});
文档与测试
1 API文档规范
- 使用OpenAPI/Swagger规范
- 记录所有请求参数
- 提供完整示例
# api-docs.yaml
/users:
get:
summary: 获取用户列表
parameters:
- name: page
in: query
description: 页码
schema:
type: integer
- name: limit
in: query
description: 每页数量
schema:
type: integer
responses:
'200':
description: 成功
content:
application/json:
schema:
type: object
properties:
code:
type: integer
data:
type: array
2 测试规范
// 使用PHPUnit进行API测试
class UserApiTest extends PHPUnit\Framework\TestCase
{
protected $client;
protected function setUp(): void
{
$this->client = new GuzzleHttp\Client([
'base_uri' => 'https://api.example.com/v1/',
'headers' => [
'Authorization' => 'Bearer ' . $this->getToken()
]
]);
}
public function testCreateUser()
{
$response = $this->client->post('users', [
'json' => [
'name' => 'Test User',
'email' => 'test@gmail.com',
'password' => 'test123456'
]
]);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode($response->getBody(), true);
$this->assertEquals('success', $data['message']);
}
}
验收清单
- [ ] 使用HTTPS协议
- [ ] URL使用复数名词
- [ ] 正确使用HTTP方法
- [ ] 统一响应格式
- [ ] 包含分页元数据
- [ ] 实现请求验证
- [ ] 返回明确错误信息
- [ ] 实现JWT认证
- [ ] 配置CORS
- [ ] 实现限流机制
- [ ] 编写API文档
- [ ] 添加单元测试
通过遵循这些规范,可以构建出可维护、可扩展且安全的RESTful API,为前端和其他系统提供清晰、一致的接口服务。
