Laravel路由模型绑定自定义异常:如何返回ProductNotFoundException?
嘿,完全不用放弃路由模型绑定,只要重写异常处理器的逻辑就能完美实现你的需求!下面是具体的操作步骤:
1. 先确认自定义异常已就绪
首先确保你的ProductNotFoundException已经正确创建在app/Exceptions目录下,示例代码如下:
namespace App\Exceptions; use Exception; class ProductNotFoundException extends Exception { public function __construct() { parent::__construct('指定的商品不存在或已被删除'); } }
你可以根据需求自定义异常消息,或者添加其他逻辑。
2. 重写异常处理器的render方法
打开app/Exceptions/Handler.php,我们需要在这里捕获路由模型绑定失败时Laravel抛出的默认异常,然后替换成你的自定义异常。
Laravel在路由模型绑定失败时,默认会抛出Illuminate\Database\Eloquent\ModelNotFoundException——不管是商品不存在,还是商品被软删除(因为默认路由绑定会忽略软删除记录),都会触发这个异常。我们要做的就是捕获它,判断是不是Product模型的绑定失败,再抛出自己的异常。
修改Handler类的render方法:
use App\Exceptions\ProductNotFoundException; use Illuminate\Database\Eloquent\ModelNotFoundException; public function render($request, Throwable $e) { // 捕获模型绑定失败的默认异常 if ($e instanceof ModelNotFoundException) { // 获取绑定失败的模型类 $modelClass = $e->getModel(); // 确认是Product模型的绑定失败 if ($modelClass === \App\Models\Product::class) { // 抛出自定义异常 throw new ProductNotFoundException(); } } // 其他异常交给Laravel默认处理逻辑 return parent::render($request, $e); }
3. (可选)给自定义异常设置专属响应
如果你想让这个自定义异常返回特定格式的响应(比如API返回JSON,页面返回自定义404视图),可以在Handler的register方法里添加以下逻辑:
public function register() { $this->renderable(function (ProductNotFoundException $e, $request) { // 针对API请求返回JSON响应 if ($request->wantsJson()) { return response()->json([ 'message' => $e->getMessage() ], 404); } // 针对页面请求返回自定义视图 return response()->view('errors.product-not-found', [], 404); }); }
额外补充:如果需要处理包含软删除的绑定场景
如果你的需求是:允许路由绑定查询软删除的商品,但在商品被软删时依然抛出自定义异常,那可以直接在Product模型里重写resolveRouteBinding方法,这样就不需要在Handler里处理了:
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use App\Exceptions\ProductNotFoundException; class Product extends Model { use SoftDeletes; public function resolveRouteBinding($value, $field = null) { // 查询包含软删除的商品 $product = $this->where($field ?? $this->getRouteKeyName(), $value) ->withTrashed() ->first(); // 商品不存在时抛异常 if (!$product) { throw new ProductNotFoundException(); } // 商品被软删时也抛异常 if ($product->trashed()) { throw new ProductNotFoundException(); } return $product; } }
不过如果只是用默认的路由绑定(不查询软删除记录),那前面Handler的方法就完全够用了。
这样操作后,当访问product/{product}路由时,只要商品不存在或者被软删除,就会触发你自定义的ProductNotFoundException,完美替代Laravel的默认异常!
内容的提问来源于stack exchange,提问作者user4748790

