Laravel 自定义异常处理器:把异常收敛成统一 JSON 响应

test12026-10-2918 次阅读

为什么要统一异常出口

默认情况下,Laravel 在 debug 开时会吐出带栈的 HTML 错误页,关了又只给个 500。前后端分离的项目更需要一个结构稳定的 JSON 错误体:固定字段、可机的 code、可追踪的 traceId。本文重写异常处理器,把各类异常收敛成同一套响应。

一、定义统一错误结构

先约定响应形状,所有错误都走它,前端只认这一个格式。

// app/Exceptions/ApiException.php
namespace App\Exceptions;

use Exception;

class ApiException extends Exception
{
    public function __construct(
        string $message = '',
        public int $status = 400,
        public string $code = 'BAD_REQUEST',
    ) {
        parent::__construct($message, $status);
    }
}

二、重写 Handler

Laravel 11 的异常处理集中在 bootstrap/app.php 里用 withExceptions 注册。把 ApiException 映射成 JSON,其他异常也兜底成统一结构,避免泄露内部细节。

// bootstrap/app.php
->withExceptions(function ($exceptions) {
    $exceptions->respond(function ($response, $e) {
        if (app()->environment('production') && $response->getStatusCode() === 500) {
            return response()->json([
                'code'    => 'SERVER_ERROR',
                'message' => '服务器开小差了,请稍后再试',
                'traceId' => request()->header('X-Trace-Id', ''),
            ], 500);
        }
        return $response;
    });

    $exceptions->render(function (\App\Exceptions\ApiException $e) {
        return response()->json([
            'code'    => $e->code,
            'message' => $e->getMessage(),
            'traceId' => request()->header('X-Trace-Id', ''),
        ], $e->status);
    });
})

三、在业务里直接抛出

控制器和业务层不再手动拼响应,抛一个语义化的异常即可,处理器替你收口。

use App\Exceptions\ApiException;

public function show(int $id)
{
    $post = Post::find($id);
    if (!$post) {
        throw new ApiException('文章不存在', 404, 'POST_NOT_FOUND');
    }
    return response()->json(['data' => $post]);
}

四、用中间件带上 traceId

在入口中间件里生成或透传 traceId,异常响应里回带,方便链路排查。

public function handle($request, $next)
{
    $traceId = $request->header('X-Trace-Id') ?? (string) \Illuminate\Support\Str::uuid();
    $request->headers->set('X-Trace-Id', $traceId);
    $response = $next($request);
    $response->headers->set('X-Trace-Id', $traceId);
    return $response;
}

小结

统一异常处理器让“出错”也变成一种契约:无论哪层抛错,前端拿到的都是同一套 code/message/traceId。开发期能精确定位,生产期不泄露栈,排障体验会好很多。

T

test1

文章作者

为什么要统一异常出口 默认情况下,Laravel 在 debug 开时会吐出带栈的 HTML 错误页,关了又只给个 50...

分类
技术
发布时间
2026-10-29
字数
约 2147 字
阅读
18 次