Laravel的异常处理及应用

什么是异常?
就是超出正常流程的部分叫做异常。

举几个例子:

Exception\FatalThrowableError

这个虽然是Error,但如果你看一下源码就知道这个是Symfony\Component\Debug\Exception\FatalThrowableError的实例。他就是个异常

ModelNotFoundException

这个是firstOrFail或者findOrFail触发的异常。

特别是在开发的时候,经常出现这中页面,大家真的思考过为什么代码出现了问题就会自动出现这种页面?Laravel是怎么处理的?

下面以firstOrFail触发异常来说说这个页面是怎么出来的。

/**
     * Execute the query and get the first result or throw an exception.
     *
     * @param  array  $columns
     * @return \Illuminate\Database\Eloquent\Model|static
     *
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
     */
    public function firstOrFail($columns = ['*'])
    {
        if (! is_null($model = $this->first($columns))) {
            return $model;
        }

        throw (new ModelNotFoundException)->setModel(get_class($this->model));
    }

这个代码是Laravel源码中firstOrFail的处理代码,看起来很简单,没有查到模型就抛出一个ModelNotFoundException异常。
能抛出,那必然是有这个异常,咱们去看看。

/**
     * Set the affected Eloquent model and instance ids.
     *
     * @param  string  $model
     * @param  int|array  $ids
     * @return $this
     */
    public function setModel($model, $ids = [])
    {
        $this->model = $model;
        $this->ids = array_wrap($ids);

        $this->message = "No query results for model [{$model}]";

        if (count($this->ids) > 0) {
            $this->message .= ' '.implode(', ', $this->ids);
        } else {
            $this->message .= '.';
        }

        return $this;
    }

这个是不是就很明显了,这里是ModelNotFoundException异常类中的方法。其中的message就是上图的No query results for model [App/user]

那为什么抛出一个异常,就会有这个页面呢?

Exception/Handler.php中的render方法中会捕获所有的异常,所以这个抛出的异常被捕获后进行了处理。

 /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Exception $e
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $e)
    {
        return parent::render($request, $e);
    }

在继续看这个里面的方法,找出返回页面的地方。

/**
     * Create a Symfony response for the given exception.
     *
     * @param  \Exception  $e
     * @return \Symfony\Component\HttpFoundation\Response
     */
    protected function convertExceptionToResponse(Exception $e)
    {
        $e = FlattenException::create($e);

        $handler = new SymfonyExceptionHandler(config('app.debug', false));

        return SymfonyResponse::create($handler->getHtml($e), $e->getStatusCode(), $e->getHeaders());
    }

终于找到,经过个个方法的筛选(不同异常会不同处理),最终会返回SymfonyResponse其中第一个参数就是异常的页面。

阶段总结一下
Laravel的异常:创建一个自定义异常->在适合的地方进行抛出->被全局的render方法捕获->判断异常类型进行不同处理。

以上就简单的说了一下Laravel最普通的异常是怎么显示出来的。
接下来说一说怎么用起来。

举个例子:
积分商城系统下单逻辑
1、检测用户是否登陆
2、检测用户是否黑名单
3、检测商品是否有货
4、检测商品该用户是否可以购买
5、检测用户积分是否充足
6、下单

经过这么多步骤才能进行一个完成的逻辑。并且每一个检测点如果不通过,就要对每个点进行特殊的处理。比如未登录就跳转登陆页、商品没有货则跳转到商品库存不足页面等等。。

如果按照正常的逻辑,一个一个判断,然后进行处理,不是不可以。但是这样处理起来controller会特别大。而且代码读起来很困难。

这时候就需要抛出异常了。

现在咱们按照上面Laravel抛出异常的方式去做。

1、创建一个自定义异常
2、在适合的地方进行抛出
3、被全局的render方法捕获
4、判断异常类型进行不同处理。

现在先创建一个 下单异常

<?php

namespace App\Exceptions;

use Exception;

class MakeOrderException extends Exception
{
    /**
     * 报告这个异常。
     *
     * @return void
     */
    public function report()
    {
    }

    /**
     * 将异常渲染至 HTTP 响应值中。
     *
     * @param  \Illuminate\Http\Request
     * @return \Illuminate\Http\Response
     */
    public function render($request)
    {
        //这里是对异常的处理
        return response()->json(['code' => $this->getCode(), 'message' => $this->getMessage()],400);
    }
}

在适合的地方进行抛出

function makeOrder (Request $request) {
    //商品无货
    if (count($request->get('thing'))) {
        throw(new MakeOrderException('商品无货', 1001) );
    }
}

被全局的render方法捕获

/**
 * 将异常渲染至 HTTP 响应值中。
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Exception  $exception
 * @return \Illuminate\Http\Response
 */
public function render($request, Exception $exception)
{
    if ($exception instanceof \App\Exceptions\MakeOrderException)  {
        return $exception->render($request);
    }

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

在全局render中捕获这个异常,并调用这个异常类中的处理方法。

思考一个问题。为什么抛出异常就好了呢?
因为需求是一直在变的。今天商品无货可能是返回到404页面,明天老板突发奇想就要跳转到首页(举个例子而已)如果再去controller中去修改代码未免显得有些繁琐。
所以单独写异常,并单独处理。出现问题只需要修改该部分代码就可以,不会影响正常的业务流程。
而且在正常用户流程中是很难触发这些问题的。并不属于常规错误,所以写在controller会显得臃肿。

完。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,492评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,048评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,927评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,293评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,309评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,024评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,638评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,546评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,073评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,188评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,321评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,998评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,678评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,186评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,303评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,663评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,330评论 2 358

推荐阅读更多精彩内容