Laravel的异常处理及应用

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

举几个例子:

《Laravel的异常处理及应用》 Exception\FatalThrowableError

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

《Laravel的异常处理及应用》 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会显得臃肿。

完。

    原文作者:禹声
    原文地址: https://www.jianshu.com/p/efe7160ca090
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞