如在個人上一篇文中所說的同樣, 在接口的設計中, 接口的返回的數據是很是重要的, 例如沒法避免的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