如在个人上一篇文中所说的同样, 在接口的设计中, 接口的返回的数据是很是重要的, 例如没法避免的500等等, 这些都是要命的错误php
同时还有一个极大的问题, 就是在新增模块中, 例如我最近须要新增一个 elasticsearh 的分词查询模块, 这个在添加索引删除索引等等操做的时候, 是很是容易致使抛出错误异常的.html
按照日常的解决思路, 咱们可能首先就是针对异常处理直接进行使用Exception
进行捕获,/偷笑, 我在之前的时候也是很是喜欢这么作的 laravel
1) try catch
(1)git
代码案例:github
public function addIndex($data) { $params = [ 'index' => 'show', 'type' => 'store', 'body' => $data ]; try{ $this->client->index($params); }catch (\Exception $exception) { halt('参数错误的异常'); # 处理错误1 } }
在咱们初期的学习和开发当前,以上的方法的确是可行的,相似于咱们经常说的 JQ一把梭 , 上面的即是 错误一刀切,这种方式的确是可以进行解决问题, 可是很是不利于针对某个错误的处理thinkphp
例如 BadRequest400Exception
方法抛出的 getMessage()
的结果是json数据, 而 BadMethodCallException
抛出的是字符串数据 你告诉我怎么处理呢?json
因此, 衍生了以下的第二种处理错误方式php7
2) try catch
(2)app
代码案例:框架
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/这个文章
异常处理接管的代码以下
<?php /** * Created by PhpStorm. * User: 邓尘锋 * Date: 19-5-5 * Time: 上午11:13 */ namespace app\common\exception; use Exception; use think\exception\Handle; use think\Response; /** * 异常处理接管, debug关闭状态下防止抛出500错误 * 如下状况将会致使500错误抛出 * 致命错误 | 系统严重错误 | 代码的严重问题 * Class Handler * @package app\common\exception */ class Handler extends Handle { # 这里写须要进行捕获的错误加载类库 protected $handler_exceptions = [ '\app\common\exception\EsearchHandler', '\app\common\exception\SystemHandler', ]; // 重写render方法 public function render(Exception $e) { try { $isDebug = config('app.app_debug'); # 判断是不是断点模式 // # 走系统抛出的系统 // if( !request()->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);
执行父方法
这个就是, 来遍历执行是否须要捕获的错误的模块, 依次加载, 当真实的响应了错误的时候(会继承Response
方法), 就代表的确是发生错误了, 具体看下面
这里查看 elasticSearch
的方法
<?php /** * Created by PhpStorm. * User: 邓尘锋 * Date: 19-5-13 * Time: 上午9:32 */ namespace app\common\exception; // 用户接受处理ElasticSearch处理的一些错误 class EsearchHandler extends BaseException implements CustomExceptionInterface { public function handler($e, array $error_info) { $e_class = get_class($e); switch ($e_class) { case 'Elasticsearch\Common\Exceptions\UnexpectedValueException': return $this->showMsg($e->getMessage(), $error_info); break; case 'Elasticsearch\Common\Exceptions\BadRequest400Exception' : 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
<?php
/**
*/ namespace app\common\exception; /** * 定义一个异常处理接口, 只要是app\common\exception下的子类, 必须继承它 * Interface CustomExceptionInterface * @package app\common\exception */ 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 'Elasticsearch\Common\Exceptions\UnexpectedValueException': return $this->showMsg($e->getMessage(), $error_info); break; case 'Elasticsearch\Common\Exceptions\BadRequest400Exception' : 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 中, 而且响应一个你定义的异常
众所周知, 他是路由出现的异常问题是不可避免的, 咱们来这样定义
在 app\common\exception\Handler
属性 handler_exceptions
中添加 '\app\common\exception\RouteExceptionHandler'
, 而且定义他
... use think\exception\HttpException; 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