本文目录导读:

在Laravel中统一处理API异常响应格式,我推荐以下几种方案:
基础方案:自定义异常处理器
1 创建统一响应格式
// app/Exceptions/ApiResponseTrait.php
<?php
namespace App\Exceptions;
use Illuminate\Http\JsonResponse;
trait ApiResponseTrait
{
/**
* 成功响应
*/
protected function success($data = null, string $message = '操作成功', int $code = 200): JsonResponse
{
return response()->json([
'code' => $code,
'message' => $message,
'data' => $data,
'timestamp' => now()->timestamp
]);
}
/**
* 失败响应
*/
protected function error(string $message = '操作失败', int $code = 400, $data = null): JsonResponse
{
return response()->json([
'code' => $code,
'message' => $message,
'data' => $data,
'timestamp' => now()->timestamp
]);
}
/**
* 分页响应
*/
protected function paginated($paginator, string $message = '操作成功'): JsonResponse
{
return response()->json([
'code' => 200,
'message' => $message,
'data' => [
'items' => $paginator->items(),
'pagination' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
'has_more_pages' => $paginator->hasMorePages()
]
]
]);
}
}
2 自定义异常类
// app/Exceptions/ApiException.php
<?php
namespace App\Exceptions;
use Exception;
use Throwable;
class ApiException extends Exception
{
protected $data;
public function __construct(
string $message = 'API错误',
int $code = 400,
$data = null,
Throwable $previous = null
) {
parent::__construct($message, $code, $previous);
$this->data = $data;
}
public function getData()
{
return $this->data;
}
}
// 自定义业务异常
class BusinessException extends ApiException
{
public function __construct(string $message = '业务处理失败', $data = null)
{
parent::__construct($message, 422, $data);
}
}
// 权限异常
class PermissionException extends ApiException
{
public function __construct(string $message = '没有权限执行此操作', $data = null)
{
parent::__construct($message, 403, $data);
}
}
3 修改异常处理器
// app/Exceptions/Handler.php
<?php
namespace App\Exceptions;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\QueryException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\JsonResponse;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Throwable;
class Handler extends ExceptionHandler
{
use ApiResponseTrait;
protected $dontReport = [
ApiException::class,
ValidationException::class,
];
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
public function register(): void
{
$this->reportable(function (Throwable $e) {
// 日志记录
if (config('app.debug')) {
\Log::error($e->getMessage(), [
'exception' => get_class($e),
'file' => $e->getFile(),
'line' => $e->getLine(),
]);
}
});
$this->renderable(function (Throwable $e, $request) {
// 只处理API请求
if ($request->is('api/*') || $request->expectsJson()) {
return $this->handleApiException($e, $request);
}
});
}
/**
* 处理API异常
*/
protected function handleApiException(Throwable $e, $request): JsonResponse
{
// 自定义业务异常
if ($e instanceof ApiException) {
return $this->error($e->getMessage(), $e->getCode(), $e->getData());
}
// 验证异常
if ($e instanceof ValidationException) {
return $this->error(
'数据验证失败',
422,
['errors' => $e->errors()]
);
}
// 认证异常
if ($e instanceof AuthenticationException) {
return $this->error('未认证或认证已过期', 401);
}
// 授权异常
if ($e instanceof AuthorizationException) {
return $this->error('没有权限执行此操作', 403);
}
// 模型未找到
if ($e instanceof ModelNotFoundException) {
return $this->error('资源不存在', 404);
}
// 路由未找到
if ($e instanceof NotFoundHttpException) {
return $this->error('请求路径不存在', 404);
}
// 请求方法不允许
if ($e instanceof MethodNotAllowedHttpException) {
return $this->error('请求方法不允许', 405);
}
// 数据库查询异常
if ($e instanceof QueryException) {
return $this->error('数据库操作失败', 500);
}
// 其他异常
if (config('app.debug')) {
return $this->error($e->getMessage(), 500, [
'debug' => [
'exception' => get_class($e),
'trace' => $e->getTrace(),
]
]);
}
// 生产环境默认响应
return $this->error('服务器内部错误', 500);
}
}
使用中间件方案
// app/Http/Middleware/ForceJsonResponse.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class ForceJsonResponse
{
public function handle(Request $request, Closure $next)
{
$request->headers->set('Accept', 'application/json');
$response = $next($request);
// 确保所有API响应都是JSON格式
if (!$response instanceof \Illuminate\Http\JsonResponse) {
$response = response()->json([
'code' => $response->getStatusCode(),
'message' => $response->getContent(),
'data' => null
], $response->getStatusCode());
}
return $response;
}
}
高级方案:统一异常响应服务
// app/Services/ApiResponseService.php
<?php
namespace App\Services;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\MessageBag;
class ApiResponseService
{
/**
* 成功响应
*/
public function success($data = null, string $message = '操作成功'): JsonResponse
{
return $this->sendResponse($data, $message, 200, true);
}
/**
* 创建成功
*/
public function created($data = null, string $message = '创建成功'): JsonResponse
{
return $this->sendResponse($data, $message, 201, true);
}
/**
* 无内容
*/
public function noContent(string $message = '操作成功'): JsonResponse
{
return $this->sendResponse(null, $message, 204, true);
}
/**
* 错误响应
*/
public function error(string $message, int $code = 400, $errors = null): JsonResponse
{
return $this->sendResponse(null, $message, $code, false, $errors);
}
/**
* 验证错误
*/
public function validationError(MessageBag $errors): JsonResponse
{
return $this->error('数据验证失败', 422, [
'errors' => $errors->toArray()
]);
}
/**
* 发送响应
*/
protected function sendResponse($data, string $message, int $code, bool $status, $errors = null): JsonResponse
{
$response = [
'code' => $code,
'status' => $status,
'message' => $message,
'data' => $data,
'timestamp' => now()->toISOString(),
'time' => round(microtime(true) - LARAVEL_START, 3),
];
if ($errors) {
$response['errors'] = $errors;
}
return response()->json($response, $code);
}
/**
* 分页响应
*/
public function paginator($items, int $total, int $page, int $perPage): JsonResponse
{
return $this->success([
'items' => $items,
'meta' => [
'total' => $total,
'page' => $page,
'per_page' => $perPage,
'last_page' => ceil($total / $perPage),
],
]);
}
}
在控制器中使用
// app/Http/Controllers/Api/UserController.php
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Services\ApiResponseService;
use Illuminate\Http\Request;
class UserController extends Controller
{
protected $apiResponse;
public function __construct(ApiResponseService $apiResponse)
{
$this->apiResponse = $apiResponse;
}
public function index()
{
try {
$users = User::paginate(15);
return $this->apiResponse->paginator(
$users->items(),
$users->total(),
$users->currentPage(),
$users->perPage()
);
} catch (\Exception $e) {
return $this->apiResponse->error('获取用户列表失败: '.$e->getMessage());
}
}
public function store(Request $request)
{
try {
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
]);
$user = User::create($validated);
return $this->apiResponse->created($user);
} catch (ValidationException $e) {
return $this->apiResponse->validationError($e->errors());
} catch (\Exception $e) {
return $this->apiResponse->error('创建用户失败');
}
}
}
使用服务提供者
// app/Providers/ApiResponseServiceProvider.php
<?php
namespace App\Providers;
use App\Services\ApiResponseService;
use Illuminate\Support\ServiceProvider;
class ApiResponseServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('api.response', function ($app) {
return new ApiResponseService();
});
}
public function boot()
{
//
}
}
全局助手函数
// app/helpers.php
<?php
if (!function_exists('api_success')) {
function api_success($data = null, string $message = '操作成功')
{
return app('api.response')->success($data, $message);
}
}
if (!function_exists('api_error')) {
function api_error(string $message, int $code = 400, $errors = null)
{
return app('api.response')->error($message, $code, $errors);
}
}
配置异常级别
// config/api.php
<?php
return [
// 是否显示调试信息
'debug' => env('API_DEBUG', false),
// 响应格式配置
'response' => [
'include_timestamp' => true,
'include_time_cost' => true,
'snake_case' => false,
],
// 错误码配置
'error_codes' => [
'success' => 200,
'created' => 201,
'bad_request' => 400,
'unauthorized' => 401,
'forbidden' => 403,
'not_found' => 404,
'validation_error' => 422,
'server_error' => 500,
],
];
异常响应示例
// 成功响应
{
"code": 200,
"status": true,
"message": "操作成功",
"data": {
"id": 1,
"name": "Test User"
},
"timestamp": "2024-01-15T10:30:00+00:00",
"time": 12.3
}
// 验证错误响应
{
"code": 422,
"status": false,
"message": "数据验证失败",
"data": null,
"timestamp": "2024-01-15T10:30:00+00:00",
"time": 5.6,
"errors": {
"email": ["The email field is required."],
"name": ["The name field is required."]
}
}
// 服务器错误响应
{
"code": 500,
"status": false,
"message": "服务器内部错误",
"data": null,
"timestamp": "2024-01-15T10:30:00+00:00",
"time": 8.9
}
推荐方案
建议采用方案1(自定义异常处理器)+ 方案4(服务类)的组合:
- 使用
ApiException和业务异常类处理业务异常 - 使用
ApiResponseService统一响应格式 - 在
Handler中统一处理所有异常 - 在控制器中使用
try-catch或直接抛出异常
这样既保证了异常处理的统一性,又提供了灵活性,如果是团队项目,建议将响应格式做成可配置的,方便不同场景适配。