本文通过建立自定义的Exception跟Handle,从而达到自定义错误返回的效果,及拦截系统运行过程中的随机异常。
整体设计思路请参见RESTFUL错误返回设计

新建异常处理目录

在 application->common 新建目录 exception。

定义Base Exception Class

在exception目录下,新建PHP类 BaseException.php

<?php


namespace app\common\exception;


use think\Exception;

class BaseException extends Exception
{
    public $httpCode;
    public $errorCode;
    public $errorMessage;
    public $errorDetail;

    public function __construct($httpCode, $errorCode, $errorMessage, $errorDetail)
    {
        $this->httpCode = $httpCode;

        $this->errorCode = $errorCode;

        $this->errorMessage = $errorMessage;

        $this->errorDetail = $errorDetail;
    }
}

定义 Custom Exception Class 继承 BaseException

这里用UserException作为示例。为什么要取名UserException,是为了将不同的异常拆分到不同的类文件中,这样使用的时候比较方便,不会所有异常都塞在同个文件中。
在exception目录下,新建PHP类 UserException.php

<?php


namespace app\common\exception;


class UserExcepiton extends BaseException
{
    /**
     * UserExcepiton constructor.
     * @param $httpCode
     * @param $errorCode
     * @param $errorMessage
     * @param $errorDetail
     */
    public function __construct($httpCode, $errorCode, $errorMessage, $errorDetail)
    {
        parent::__construct($httpCode, $errorCode, $errorMessage, $errorDetail);
    }

    // 微信授权登录失败

    /**
     * @param $wxErrcode
     * @param $wxErrmsg
     * @throws UserExcepiton
     */
    public static function wexinAuthFail($wxErrcode, $wxErrmsg)
    {
        throw new UserExcepiton('400', 'User.01',
            '微信授权失败。', '微信错误代码:' . $wxErrcode . '微信错误信息:' . $wxErrmsg);
    }

    /**
     * @param $accessToken
     * @throws UserExcepiton
     */
    public static function notLogin($accessToken){
        throw new UserExcepiton('400', 'User.02','用户未登录。',
            'access_token:'.$accessToken);
    }
}

这里定义了两个方法,下面我们会讲到怎么抛出这两个异常,并被处理成错误返回。

定义Handle类

这个是很关键,它通过继承thinkphp提供的Exception/Handle,从而在response前拦截到所有异常并做进一步的处理。
在exception目录下,新建PHP类 CustomHandle.php

<?php


namespace app\common\exception;


use Exception;
use think\exception\Handle;

class CustomHandle extends Handle
{
  private $httpCode;
  private $errorCode;
  private $errorMessage;
  private $errorDetail;

  public function render(Exception $e)
  {
    if ($e instanceof BaseException) {
      $this->errorCode = $e->errorCode;
      $this->errorMessage = $e->errorMessage;
      $this->httpCode = $e->httpCode;
      $this->errorDetail = $e->errorDetail;
    } else {
      if (config('app_debug')) {
        return parent::render($e);
      }

      $this->httpCode = 500;
      $this->errorCode = "000000";
      $this->errorMessage = "服务器未知错误";
      $this->errorDetail = $e->getMessage();
    }

    $path = "http://" . request()->host() . ":" . request()->port() . request()->baseUrl();
    $content = request()->getContent();
    $queryString = request()->query();

    $result = [
      'path'          => $path,
      'error_code'    => $this->errorCode,
      'error_message' => $this->errorMessage,
      'error_detail'  => $this->errorDetail,
      'query_string'  => $queryString,
      'content'       => $content
    ];

    return json($result, $this->httpCode);
  }
}

说明:
1、这里通过 if ($e instanceof BaseException) 判断是否是我们自定义的异常,如果不是。那是系统抛出,说明我们遇到了未知错误(BUG)。
2、如果遇到了未知错误,还要继续 if (config('app_debug')) 判断当前是否处于调试模式,如果是就不处理,返回thinkphp的调试信息,如果不是就处理成统一的错误返回。
3、这部分程序还没有最终完成,按照设计要求,当遇到未知异常时,应该返回动态错误代码并记录系统日志,这里并没有,而是简单的返回000000。这样并不利于后期追溯问题。

最终的目录结构

thinkphp异常目录结构

抛出自定义异常

定义完上面这三个类后,我们就可以在程序中任意位置抛出这些异常,无论是db层,service层,还是controller层,都会得到响应,并且终止当前程序,向客户端返回错误信息。
下面示例是请求一个数据上传接口,在access token检验不通过时,返回用户未登录的错误信息。注意,这里的错误返回是service层check_login抛出的,而不是controller层。这也是采用异常处理机制最大的好处,就是你可以在任何地方终止掉程序。

-- Controller
/**
* @route('/drug-used-plan/upload')
*/
public
function upload()
{
    $accessToken = explode(' ', $this->request->header('Authorization'))[1];
    $loginStatus = $this->userService->check_login($accessToken);
    $userId = $loginStatus->user_id;

    $drug_plan = $this->request->post('drug_plan');

    $this->drugUsedPlanService->uploadPlan($userId, $drug_plan);

    return json(CustomResponse::commonResponse());
}

-- Service
public function check_login($accessToken)
{
    $loginStatus = $this->getLoginStatusFromCache($accessToken);

    if ($loginStatus) {
        return $loginStatus;
    } else {
        // 抛出异常
        throw UserExcepiton::notLogin($accessToken);
    }
}

版权声明:如无特别声明,本文版权归 一年四季 所有,转载请注明本文链接。

(采用 CC BY-NC-SA 4.0 许可协议进行授权)

本文标题:《 自定义thinkphp5.1异常 》

本文链接:https://www.yucanlin.cn/develop/%E8%87%AA%E5%AE%9A%E4%B9%89thinkphp5%E5%BC%82%E5%B8%B8.html