资讯专栏INFORMATION COLUMN

带你抛出优雅的处理系统异常

susheng / 911人阅读

摘要:关于抛出异常如在我的上一篇文中所说的一样在接口的设计中接口的返回的数据是非常重要的例如无法避免的等等这些都是要命的错误同时还有一个极大的问题就是在新增模块中例如我最近需要新增一个的分词查询模块这个在添加索引删除索引等等操作的时候是非常容易导

关于抛出异常

如在我的上一篇文中所说的一样, 在接口的设计中, 接口的返回的数据是非常重要的, 例如无法避免的500等等, 这些都是要命的错误

同时还有一个极大的问题, 就是在新增模块中, 例如我最近需要新增一个 elasticsearh 的分词查询模块, 这个在添加索引删除索引等等操作的时候, 是非常容易导致抛出错误异常的.

按照平常的解决思路, 我们可能首先就是针对异常处理直接进行使用Exception进行捕获,/偷笑, 我在以前的时候也是非常喜欢这么做的

1) try catch (1)

代码案例:

public function addIndex($data)
{
    $params = [
        "index" => "show",
        "type" => "store",
        "body" => $data
    ];

    try{
        $this->client->index($params);
    }catch (Exception $exception) {
        halt("参数错误的异常"); # 处理错误1
    }
}

在我们初期的学习和开发当前,以上的方法的确是可行的,类似于我们常常说的 JQ一把梭 , 上面的便是 错误一刀切,这种方式的确是能够进行解决问题, 但是非常不利于针对某个错误的处理

例如 BadRequest400Exception 方法抛出的 getMessage() 的结果是json数据, 而 BadMethodCallException 抛出的是字符串数据 你告诉我怎么处理呢?

所以, 衍生了如下的第二种处理错误方式

2) try catch (2)

代码案例:

public function addIndex($data)
{
    $params = [
        "index" => "show",
        "type" => "store",
        "body" => $data
    ];

    try{
        $this->client->index($params);
    }catch (BadRequest400Exception $e) {
        $msg = json_decode($e->getMessage(), true);
        halt("参数错误的异常:" . $msg); # 处理错误1
    }catch (BadMethodCallException $e){
        halt("方法不存在的错误异常:" . $e->getMessage()); # 处理错误2
    }catch(Exception $e) {
        halt("系统异常");
    }
}

恩, 的确,以上也算是我们平常常见的一种解决方式, 但是, 有没有考虑过代码冗余呢, 如果10个需要捕获错误的方法, 难道就写10个吗 . 这个时候,我们需要一个 耦合掉这些方法的操作 , 尽管php7中错误异常的支持越来越强大, 但面对业务场景的不断增加,我们必须采取 可靠 稳定 了解 的三种标准来要求它. 了解的意思是, 能够给我们输出清楚的错误日志等

步入正题

开发框架是thinkphp5.1

异常处理接管

thinkphp给我们提供了强大的异常处理机制, 当然市场上的框架基本都能提供这个功能. 我们找到文档错误处理的那一块, (https://www.kancloud.cn/manua...[https://www.kancloud.cn/manual/thinkphp5_1/354092#_42]

基于tp自带的, 再其基础上进行优化更加丰富的自定义异常处理, 这便是我所理解的异常处理接管吧

进行业务设计

定义个异常处理接管, 重写render方法 , 这个方法主要是抛出一个响应异常 .

注意注意 ~~!!

查看源码还有我的经验得知, 它必须是 return 的方式 , 返回一个 Response 创建的响应异常, 否则会给你抛出莫名其妙的错误

阅读如下文章, 请先查看 http://surest.cn/archives/71/这个文章

主要是了解一下 https://github.com/surest-sky/example/blob/master/laravel%E7%9A%84%E4%B8%80%E4%BA%9B%E5%86%99%E6%B3%95/ApiResponse_tp.php的用法

异常处理接管的代码如下

isAjax() || $isDebug) {
//                return parent::render($e);
//            }

            # 错误的信息, 用于写入日志
            $error_info = [
                "code" => $e->getCode(), # 错误代码描述
                "line" => $e->getLine(), # 错误代码行
                "message" => $e->getMessage(), # 错误详细信息
                "file" => $e->getFile() # 错误文件名称
            ];

        # 捕获错误处理异常
        return $this->handler_exception($e, $error_info);

        }catch (Exception $exception) {
            return parent::render($exception);
        }
    }

    /**
    * 加载错误处理
    * @param $e
    */
    public function handler_exception($e, $error_info)
    {
        foreach ($this->handler_exceptions as $exception) {
            $exception = new $exception;
            if($exception->handler($e, $error_info) instanceof Response){
                return $exception->handler($e, $error_info);
            }
        }
    }
}

它的流程大概是

handler_exceptions: 定义需要捕获异常的错误处理模块

render : 重写错误处理异常

error_info 用来分发给各个子模块异常, 用来需要的地方进行打印日志

catch (Exception $exception) : 这个的用处不大, 主要是这个如果出现错误的话,自己处理不了, 就交给tp自带的异常处理去处理 return parent::render($exception); 执行父方法

handler_exception

这个就是, 来遍历执行是否需要捕获的错误的模块, 依次加载, 当真实的响应了错误的时候(会继承Response方法), 就表明的确是发生错误了, 具体看下面

功能模块的异常处理

这里查看 elasticSearch 的方法

showMsg($e->getMessage(), $error_info);
                break;
            case "ElasticsearchCommonExceptionsBadRequest400Exception" :
                return $this->showMsg(json_decode($e->getMessage(), true), $error_info);
                break;
        }

        # 否则不返回错误异常
    }
}

流程解释, 如上可以看到, 我们继承了 BaseException 并且实现了 CustomExceptionInterface 接口

BaseException

namespace appcommonexception;
use appcommonTraitsApiResponse;
class BaseException
{

use ApiResponse;

public function __construct()
{
    # 这个必须强制设置为true
    $this->is_anomaly_andling_takeover = true;

    # 检查当前异常处理是否继承了异常处理接管, 没有则抛出一个异常
    if(!($this instanceof CustomExceptionInterface)) {
        return $this->showMsg(__CLASS__ . "必须继承CustomExceptionInterface这个接口", []);
    }
}

public function showMsg($msg, array $error_info, $code = 500)
{
    return $this->status($msg, compact("error_info"), $code, $code);
}

}

CustomExceptionInterface

/**

Created by PhpStorm.

User: 邓尘锋

Date: 19-5-13

Time: 上午9:35

        */
        namespace appcommonexception;
        /**
        * 定义一个异常处理接口, 只要是appcommonexception下的子类, 必须继承它
        * Interface CustomExceptionInterface
        * @package appcommonexception
        */
        Interface CustomExceptionInterface {
            public function handler($e, array $error_info); # 接受异常处理

            public function showMsg($msg, array $error_info, $code); # 抛出错误消息
        }

流程解释 :

BaseException 使用了 ApiResponse 用于抛出异常,

定义的 showMsg 是为了实现 CustomExceptionInterface 接口, 作用在于返回一个 response 的错误

$this->is_anomaly_andling_takeover 这个是为了重写定义 ApiResponse 的响应, 在原先的 ApiResponse 中

    public function respond($data, $header = [])
    {
        $type = request()->isAjax() ? "json" : "html";

        $response = JsonResponse::create($data, $type, $this->code, $header);

        throw new HttpResponseException($response);
    }

他是直接抛出的 HttpResponseException 异常的错误, 显然不符合我们之前所说的 它必须是 return 的方式 , 返回一个 Response 创建的响应异常, 否则会给你抛出莫名其妙的错误, 所以重新定义属性

public function respond($data, $header = [])
{

    $type = request()->isAjax() ? "json" : "html";

    $response = JsonResponse::create($data, $type, $this->code, $header);

    if( $this->is_anomaly_andling_takeover ) {
        return $response; # 抛出 response 异常
    }
    throw new HttpResponseException($response);
}

这样 showMsg 方法就返回的是 response 异常了

在子类 handler 方法中, 就可以轻松的定义你的错误异常咯

    public function handler($e, array $error_info)
    {
        $e_class = get_class($e);

        switch ($e_class) {
            case "ElasticsearchCommonExceptionsUnexpectedValueException":
                return $this->showMsg($e->getMessage(), $error_info);
                break;
            case "ElasticsearchCommonExceptionsBadRequest400Exception" :
                return $this->showMsg(json_decode($e->getMessage(), true), $error_info);
                break;
        }

        # 否则不返回错误异常
    }

如之前所说的, 我们可以把添加所以那一串代码演化成

public function addIndex($data)
{
    $params = [
        "index" => "show",
        "type" => "store",
        "body" => $data
    ];

    $this->client->index($params);

}

再也不需要进行捕获了, 如果它抛出错误了, 会自动走到 handler 中, 并且响应一个你定义的异常

再添加一个错误处理

众所周知, 他是路由出现的异常问题是不可避免的, 我们来这样定义

appcommonexceptionHandler 属性 handler_exceptions 中添加 "appcommonexceptionRouteExceptionHandler", 并且定义他

...

use thinkexceptionHttpException;

class RouteExceptionHandler extends BaseException implements CustomExceptionInterface
{

public function handler($e, array $err_info)
{
    # 检测理由错误
    if( $e instanceof HttpException) {
        return $this->showMsg("当前请求路由不存在", $err_info, 404);
    }
}

即可

响应结果:

{
    "msg": "当前请求路由不存在",
    "code": 404,
    "error_info": {
        "code": 0,
        "line": 63,
        "message": "module not exists:store",
        "file": "/www/wwwroot/app/thinkphp/library/think/route/dispatch/Module.php"
    }
}
完结

代码实例在

https://github.com/surest-sky/example/tree/master/exception

来源

邓尘锋

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/31444.html

相关文章

  • @Validated和@Valid区别?校验级联属性(内部类)

    摘要:毕竟永远相信本文能给你带来意想不到的收获使用示例关于数据校验这一块在中的使用案例,我相信但凡有点经验的程序员应该没有不会使用的,并且还不乏熟练的选手。 每篇一句 NBA里有两大笑话:一是科比没天赋,二是詹姆斯没技术 相关阅读 【小家Java】深入了解数据校验:Java Bean Validation 2.0(JSR303、JSR349、JSR380)Hibernate-Validati...

    Winer 评论0 收藏0
  • Java 异常处理 9 个最佳实践

    摘要:异常处理的个最佳实践原文地址翻译出处在中,异常处理是个很麻烦的事情。使用描述性消息抛出异常这个最佳实践背后的想法与前两个类似。当你以错误的格式提供时,它将被类的构造函数抛出。类提供了特殊的构造函数方法,它接受一个作为参数。 Java 异常处理的 9 个最佳实践 原文地址:https://dzone.com/articles/9-...翻译出处:https://www.oschina.n...

    sihai 评论0 收藏0
  • [译]带你理解 Async/await

    摘要:所以是在一秒后显示的。这个行为不会耗费资源,因为引擎可以同时处理其他任务执行其他脚本,处理事件等。每个回调首先被放入微任务队列然后在当前代码执行完成后被执行。,函数是异步的,但是会立即运行。否则,就返回结果,并赋值。 「async/await」是 promises 的另一种更便捷更流行的写法,同时它也更易于理解和使用。 Async functions 让我们以 async 这个关键字开...

    xiaochao 评论0 收藏0
  • 关于thinkphp5手动抛出Http异常时自定义404页面报错问题

    摘要:在使用手动抛出异常时,希望跳转到自定义的错误页面,官方的文章中是这样描述的。只能看源码找问题了。而这个布局文件的路径是一个相对路径,这时如果你抛出异常的地方不是在的里,就找不到布局文件了。在使用HttpException手动抛出异常时,希望跳转到自定义的错误页面,官方的文章中是这样描述的。 可以使用thinkexceptionHttpException类来抛出异常 // 抛出 HTTP 异...

    番茄西红柿 评论0 收藏0
  • 阿里小哥带你玩转JVM:揭秘try-catch-finally在JVM底层都干了些啥?

    摘要:当触发异常的字节码的索引值在某个异常表条目的监控范围内,虚拟机会判断所抛出的异常和该条目想要捕获的异常是否匹配。 作者:李瑞杰目前就职于阿里巴巴,狂热JVM爱好者让我们准备一个函数:showImg(https://user-gold-cdn.xitu.io/2019/5/19/16acbce35adfefb7);然后,反编译他的字节码:showImg(https://user-gold-cd...

    番茄西红柿 评论0 收藏0

发表评论

0条评论

susheng

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<