PHP 接口开发规范:统一返回格式、异常处理与参数校验

PHP 接口开发规范:统一返回格式、异常处理与参数校验

在中大型 PHP 项目中,接口的一致性直接决定了前端联调效率、问题排查速度和系统可维护性。很多团队在初期"怎么快怎么写",结果后期陷入"一个接口一种返回格式"的泥潭。

本文将围绕 统一返回格式、全局异常处理、参数校验​ 三大核心,给出一套可落地的 PHP 接口开发规范,适用于 Laravel、ThinkPHP、Hyperf、原生 PHP 等主流框架。


一、为什么需要接口规范?

混乱接口的常见症状:

  • 有的返回 {code:0, data:{}},有的返回 {error_code:200, result:{}}

  • 成功返回 HTTP 200,业务失败却返回 HTTP 500

  • 错误信息一会儿是中文,一会儿是英文

  • 参数错误只返回 "error",没有字段名

  • 异常直接抛给前端,暴露 SQL、路径等敏感信息

后果

  • 前端需要为不同接口写不同解析逻辑

  • 联调成本高,沟通频繁

  • 线上问题难以快速定位


二、统一返回格式规范

1. 标准 JSON 返回结构

推荐使用如下结构:

复制代码
{
    "code": 0,
    "message": "success",
    "data": {},
    "timestamp": 1700000000
}

字段说明

字段 类型 必填 说明
code int 业务状态码(非 HTTP 状态码)
message string 提示信息
data mixed 业务数据(成功时返回)
timestamp int 响应时间戳(秒)

2. 业务状态码设计建议

code 含义
0 成功
400xx 客户端错误(参数、权限等)
40100 未登录
40300 无权限
40400 资源不存在
42200 参数校验失败
500xx 服务端错误
50001 系统异常
50002 数据库异常

⚠️ 注意:code ≠ HTTP 状态码

HTTP 状态码负责传输层语义,code 负责业务语义。


3. 成功返回示例

复制代码
{
    "code": 0,
    "message": "success",
    "data": {
        "id": 1,
        "username": "zhangsan"
    },
    "timestamp": 1700000000
}

空数据统一返回:

复制代码
{
    "code": 0,
    "message": "success",
    "data": {},
    "timestamp": 1700000000
}

4. 失败返回示例

复制代码
{
    "code": 42200,
    "message": "参数校验失败",
    "data": {
        "username": "用户名不能为空"
    },
    "timestamp": 1700000000
}

三、全局异常处理规范

1. 异常分类设计

推荐分层异常体系:

复制代码
Throwable
├── Exception
│   ├── BusinessException      # 业务异常(可预期)
│   ├── ValidateException     # 参数校验异常
│   ├── AuthException         # 认证异常
│   └── SystemException       # 系统异常(不可预期)

2. 自定义业务异常(示例)

复制代码
namespace App\Exceptions;

use RuntimeException;

class BusinessException extends RuntimeException
{
    protected int $code = 50001;

    public function __construct(string $message = '', int $code = 0)
    {
        parent::__construct($message ?: '业务处理失败', $code ?: $this->code);
    }
}

3. 全局异常捕获(以 Laravel 为例)

app/Exceptions/Handler.php

复制代码
public function render($request, Throwable $e)
{
    if ($request->expectsJson()) {
        return $this->handleApiException($e);
    }

    return parent::render($request, $e);
}

protected function handleApiException(Throwable $e)
{
    $code = method_exists($e, 'getCode') ? $e->getCode() : 50000;
    $message = $e->getMessage();

    // 兜底处理
    if ($e instanceof \Error || $e instanceof \TypeError) {
        $code = 50001;
        $message = '系统内部错误';
    }

    return response()->json([
        'code' => $code,
        'message' => $message,
        'data' => [],
        'timestamp' => time(),
    ], $this->getHttpStatus($code));
}

protected function getHttpStatus(int $code): int
{
    return match (true) {
        $code >= 40000 && $code < 50000 => 400,
        default => 500,
    };
}

✅ 好处:

  • 前端无需区分异常类型

  • 敏感错误信息不会泄露

  • 所有接口返回格式一致


4. 异常使用规范

✅ 正确做法:

复制代码
if (!$user) {
    throw new BusinessException('用户不存在', 40400);
}

❌ 错误做法:

复制代码
return json_encode(['error' => 'fail']);

四、参数校验规范

1. 校验原则

  • 所有外部参数必须校验

  • 校验逻辑集中处理,不在 Controller 里写大量 if

  • 校验失败抛异常,不走业务逻辑


2. 基础校验示例(原生 PHP)

复制代码
$data = $_POST;

if (!isset($data['username']) || trim($data['username']) === '') {
    throw new ValidateException('用户名不能为空', 42200);
}

if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
    throw new ValidateException('邮箱格式不正确', 42200);
}

3. Laravel 校验(推荐)

复制代码
public function store(Request $request)
{
    $validated = $request->validate([
        'username' => 'required|string|min:3|max:20',
        'email'    => 'required|email',
        'age'      => 'integer|min:1|max:120',
    ]);

    // 校验通过后直接使用 $validated
}

统一返回格式示例:

复制代码
{
    "code": 42200,
    "message": "参数校验失败",
    "data": {
        "username": "用户名必须为字符串",
        "email": "邮箱格式不正确"
    },
    "timestamp": 1700000000
}

4. ThinkPHP 校验

复制代码
$validate = new \think\Validate([
    'username' => 'require|length:3,20',
    'email'    => 'require|email',
]);

if (!$validate->check($this->request->post())) {
    throw new ValidateException(
        '参数校验失败',
        42200,
        $validate->getError()
    );
}

5. 自定义校验规则

例如:手机号校验

复制代码
Validator::extend('mobile', function ($attribute, $value) {
    return preg_match('/^1[3-9]\d{9}$/', $value);
});

五、Controller 层最佳实践

推荐 Controller 结构

复制代码
class UserController
{
    public function store(UserRequest $request, UserService $service)
    {
        $result = $service->create($request->validated());

        return $this->success($result);
    }

    protected function success($data = [], string $message = 'success')
    {
        return response()->json([
            'code' => 0,
            'message' => $message,
            'data' => $data,
            'timestamp' => time(),
        ]);
    }
}

✅ Controller 只做三件事:

  1. 接收请求

  2. 参数校验

  3. 调用 Service

  4. 返回统一格式


六、HTTP 状态码使用建议

场景 HTTP 状态码 code
成功 200 0
参数错误 400 42200
未登录 401 40100
无权限 403 40300
资源不存在 404 40400
系统异常 500 50001

❗ 不要把业务错误全部返回 200,也不要把系统异常返回 200。


七、接口规范速查表 ✅

项目 规范
返回格式 JSON,统一结构
code 业务状态码,0 表示成功
异常 全局捕获,统一返回
参数校验 集中处理,失败抛异常
Controller 薄层,不写业务逻辑
错误信息 友好、可控、不暴露系统细节

八、总结

一套好的接口规范,核心价值在于:

  • ✅ 前端不再"猜接口"

  • ✅ 后端异常可控、可追踪

  • ✅ 新成员快速上手

  • ✅ 系统长期可维护

接口一致性,不是美观问题,而是工程质量问题。

相关推荐
AI视觉网奇16 分钟前
动作识别 视频理解大模型
开发语言·python·音视频
SomeB1oody42 分钟前
【RustyML入门】3.8. 正则化与归一化层
开发语言·后端·机器学习·rust·教程
__zRainy__1 小时前
Node系列 · Node基础:全局变量与全局对象
开发语言·前端·javascript
鬼手点金1 小时前
Scrapy + Playwright 完整示例(JS 动态渲染网页)
开发语言·javascript·爬虫·python·scrapy·html·json
Mr. zhihao1 小时前
深度解析:为什么Java序列化需要搭配ByteArrayOutputStream?IO装饰器模式的精妙设计
java·开发语言·装饰器模式
格林威2 小时前
多相机并行采图最佳实践:Task.WhenAll + 异常处理 + 资源释放
开发语言·人工智能·数码相机·计算机视觉·c#·视觉检测·机器视觉
djjjx.2 小时前
【 C++ 】多态
开发语言·c++·多态
夜雪一千2 小时前
Python如何使用XPath定位没有特征的元素?无id、无class通用定位技巧
开发语言·python
艾莉丝努力练剑2 小时前
【QT:解决问题】Qt5Core.dll:无法定位程序输入点
java·开发语言·qt·学习·面试
流浪0012 小时前
Python 基础语法(一):常量、变量、输入输出与运算符
开发语言·python