You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel控制器方法统一异常处理:模拟Python装饰器实现问询

在Laravel中实现控制器方法的异常捕获"装饰器"

PHP确实没有Python那种原生@装饰器语法,但Laravel有好几招能实现类似效果,不用在每个控制器方法里重复写try-catch:

方法一:用路由中间件搞定

搞个中间件,专门抓指定路由或方法的异常,返回统一格式:

  1. 先创建中间件
php artisan make:middleware CatchMethodExceptions
  1. 写中间件核心逻辑
    打开app/Http/Middleware/CatchMethodExceptions.php,把handle方法改成这样:
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Response;
use Throwable;

class CatchMethodExceptions
{
    public function handle($request, Closure $next)
    {
        try {
            return $next($request);
        } catch (Throwable $e) {
            // 自定义你要返回的结构
            return response()->json([
                'code' => Response::HTTP_INTERNAL_SERVER_ERROR,
                'message' => '服务器内部错误',
                'error' => env('APP_DEBUG') ? $e->getMessage() : ''
            ], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }
}
  1. 注册中间件
    去app/Http/Kernel.php的$routeMiddleware数组里加一行:
'catch.exceptions' => \App\Http\Middleware\CatchMethodExceptions::class,
  1. 给目标路由/方法绑中间件
  • 单个路由绑定:
Route::get('/user/{id}', [UserController::class, 'show'])->middleware('catch.exceptions');
  • 控制器内只给特定方法加:
    在控制器的__construct里指定就行:
public function __construct()
{
    // 只给show和store方法加
    $this->middleware('catch.exceptions')->only(['show', 'store']);
    // 要是想排除某些方法就用->except(['index'])
}

方法二:用Trait复用捕获逻辑

要是只想给某几个控制器的特定方法加,整个Trait最方便:

  1. 创建Trait文件
    在app/Traits目录下建个CatchExceptions.php:
<?php

namespace App\Traits;

use Illuminate\Http\Response;
use Throwable;

trait CatchExceptions
{
    protected function catchExceptions(callable $callback)
    {
        try {
            return $callback();
        } catch (Throwable $e) {
            return response()->json([
                'code' => Response::HTTP_INTERNAL_SERVER_ERROR,
                'message' => '操作失败',
                'error' => env('APP_DEBUG') ? $e->getMessage() : ''
            ], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }
}
  1. 在控制器里用
<?php

namespace App\Http\Controllers;

use App\Traits\CatchExceptions;
use Illuminate\Http\Request;

class UserController extends Controller
{
    use CatchExceptions;

    public function show($id)
    {
        return $this->catchExceptions(function () use ($id) {
            // 原来的方法逻辑,比如查用户
            $user = User::findOrFail($id);
            return response()->json(['data' => $user]);
        });
    }
}

方法三:全局异常处理(批量搞定)

要是想所有控制器的异常都统一返回格式,改Laravel自带的异常处理器就行:

打开app/Exceptions/Handler.php,修改render方法:

<?php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Response;
use Throwable;

class Handler extends ExceptionHandler
{
    // ... 保留原来的其他代码

    public function render($request, Throwable $e)
    {
        // 只给API请求返回统一格式
        if ($request->expectsJson()) {
            $code = $this->isHttpException($e) ? $e->getStatusCode() : Response::HTTP_INTERNAL_SERVER_ERROR;
            $message = $this->isHttpException($e) ? $e->getMessage() : '服务器内部错误';
            
            return response()->json([
                'code' => $code,
                'message' => $message,
                'error' => env('APP_DEBUG') ? $e->getMessage() : ''
            ], $code);
        }

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

这种方式适合全局统一处理,不用逐个方法加;要是需要针对部分方法特殊处理,结合前两种方式更灵活。


内容的提问来源于stack exchange,提问作者Danilo Toro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 21:10:31