本文目录导读:

在PHP中统一返回数据格式是一个很好的实践,它能让前后端协作更顺畅,也便于后期的维护和扩展,下面提供几种常见且实用的统一方案:
JSON 格式(最推荐,适用于现代 Web 应用/API)
这是目前使用最广泛的方式,特别适合前后端分离的项目。
定义一个统一的返回函数
<?php
/**
* 统一返回 JSON 数据格式
*
* @param int $code 状态码(200 成功,400 参数错误,401 未登录,404 不存在,500 服务器错误)
* @param string $message 提示信息
* @param mixed $data 业务数据(可选,默认 null)
* @param array $extra 额外字段(可选,用于扩展一些头信息等)
* @return \Illuminate\Http\JsonResponse|\think\response\Json
*/
function apiResponse($code = 200, $message = 'success', $data = null, $extra = [])
{
// 1. 构建统一的数据结构
$response = [
'code' => $code, // 业务状态码
'message' => $message, // 提示信息
'data' => $data, // 业务数据
'timestamp' => time(), // 时间戳,便于调试
];
// 2. 如果有额外的自定义字段,合并进去
$response = array_merge($response, $extra);
// 3. 返回 JSON 响应
return json($response); // Laravel 写法
// 或者 return response()->json($response); // 通用 PHP 写法
// 或者 return json_encode($response); // 纯 PHP 写法,需配合 header 设置
}
使用示例
// 成功返回(带数据) return apiResponse(200, '获取成功', ['id' => 1, 'name' => '张三']); // 成功返回(无数据) return apiResponse(200, '删除成功'); // 失败返回 return apiResponse(400, '参数错误:username 不能为空'); // 未授权 return apiResponse(401, '请先登录'); // 服务器错误 return apiResponse(500, '系统繁忙,请稍后再试');
对应的前端解析
// 前端 fetch 或 axios 解析
fetch('/api/user/1')
.then(res => res.json())
.then(result => {
if (result.code === 200) {
console.log('成功:', result.data);
} else {
console.log('失败:', result.message);
}
});
使用自定义类或 Trait(适合大型项目)
可以将返回逻辑封装成类,更规范,也方便 IDE 提示。
<?php
namespace App\Traits;
trait ApiResponse
{
/**
* 成功响应
*/
protected function success($data = null, $message = 'success', $code = 200)
{
return $this->response($code, $message, $data);
}
/**
* 失败响应
*/
protected function error($message = 'error', $code = 400, $data = null)
{
return $this->response($code, $message, $data);
}
/**
* 核心响应方法
*/
protected function response($code, $message, $data)
{
return response()->json([
'code' => $code,
'message' => $message,
'data' => $data,
'timestamp' => time(),
]);
}
/**
* 列表分页响应(常用)
*/
protected function paginate($paginator, $message = 'success')
{
return $this->success([
'items' => $paginator->items(),
'total' => $paginator->total(),
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'last_page' => $paginator->lastPage(),
], $message);
}
}
处理 HTTP 状态码
除了业务状态码,还应该将 HTTP 状态码与业务状态码对应起来,这样浏览器和前端都能正确识别。
| 场景 | 业务码 (code) | HTTP 状态码 |
|---|---|---|
| 成功 | 200 | 200 |
| 参数校验失败 | 400 | 200/400 |
| 未登录/登录过期 | 401 | 200/401 |
| 无权限 | 403 | 200/403 |
| 资源不存在 | 404 | 200/404 |
| 服务器内部错误 | 500 | 200/500 |
注意: 通常业务码用于前端逻辑判断,HTTP 状态码用于浏览器和网络层面的判断,现在很多团队约定统一返回
200 HTTP 状态码+ 业务码,这样前端统一走success回调;也可以 HTTP 状态码和业务码都真实返回,前端捕获错误更规范。
// 推荐:两者都真实对应
public function login(Request $request)
{
if (!auth()->attempt($request->only('email','password'))) {
return response()->json([
'code' => 401,
'message' => '账号或密码错误',
'data' => null,
], 401); // 同时设置 HTTP 状态码为 401
}
$user = auth()->user();
return response()->json([
'code' => 200,
'message' => '登录成功',
'data' => ['token' => $user->createToken('auth')->plainTextToken],
], 200);
}
全局异常处理器(统一兜底)
无论代码哪里出了异常,都统一走这个格式返回,避免散落的错误格式。
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler
{
public function render($request, Throwable $e)
{
// 如果是 API 请求,统一返回 JSON
if ($request->expectsJson() || $request->is('api/*')) {
$code = $e->getCode() && in_array($e->getCode(), [200, 400, 401, 403, 404, 422, 500]) ? $e->getCode() : 500;
return response()->json([
'code' => $code,
'message' => $e->getMessage(),
'data' => null,
'timestamp' => time(),
], $code === 500 ? 500 : 200); // 500 用真实 HTTP 状态码,其它统一 200
}
return parent::render($request, $e);
}
}
总结建议
| 项目类型 | 推荐方案 |
|---|---|
| 前后端分离的 Web 应用 | 方案一 + 方案三(JSON + 业务码) |
| 大型后台管理系统 | 方案二(Trait 方式) |
| 高并发、微服务 API | 方案一 + 引入统一 SDK 或中间件 |
| 所有项目 | 确保全局异常统一 |
核心要点:
code:业务状态码,用于前端逻辑判断message:人类可读的提示信息data:业务数据(成功)、null(失败)timestamp:时间戳,便于排查问题- HTTP 状态码尽量与业务码一致或至少保证 500 的错误正确返回