Laravel的异常处理及应用

作者: 禹声 | 来源:发表于2018-10-15 15:43 被阅读20次

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

    举几个例子:

    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中去修改代码未免显得有些繁琐。
    所以单独写异常,并单独处理。出现问题只需要修改该部分代码就可以,不会影响正常的业务流程。

    完。

    相关文章

      网友评论

        本文标题:Laravel的异常处理及应用

        本文链接:https://www.haomeiwen.com/subject/vcbfzftx.html