Webman实战教程:Exception异常插件如何解决开发中的异常问题

2023-10-22 19:02:50 浏览数 (2)

异常和错误

PHP中的异常的独特性,即PHP中的异常不同于主流语言C 、java中的异常。在Java中,异常是唯一的错误报告方式,而在PHP中却不是这样,而是把所有不正常的情况都视作了错误进行处理。这两种语言对异常和错误的界定存在分歧。什么是异常什么是错误,两种语言的设计者存在不同的观点。

PHP中的异常

是程序在运行中出现不符合预期的情况及与正常流程不同的状况。一种不正常的情况,按照正常逻辑本不该出的错误,但仍然会出现的错误,这是属于逻辑和业务流程的错误,而不是编译或者语法上的错误。

PHP中的错误

是属于php脚本自身的问题,大部分情况是由错误的语法,服务器环境导致,使得编译器无法通过检查,甚至无法运行的情况。warning、notice都是错误,只是他们的级别不同而已,并且错误是不能被try-catch捕获的。

在PHP中遇到任何自身错误都会触发一个错误,而不是抛出异常。PHP一旦遇到非正常代码,通常都会触发错误,而不是抛出异常。因此,如果想要使用异常处理不可预料的问题,是办不到的。

安装

代码语言:javascript复制
composer require tinywan/exception-handler

使用

配置

config/exception.php

代码语言:javascript复制
return [
    // 这里配置异常处理类
    '' => TinywanExceptionHandlerHandler::class,
];

webman-admin中配置

异常接管

/plugin/admin/config/exception.php

代码语言:javascript复制
return [
    '' => TinywanExceptionHandlerHandler::class,
];

接口不存在路由处理

/plugin/admin/config/route.php

代码语言:javascript复制
Route::fallback(function () {
    throw new TinywanExceptionHandlerExceptionRouteNotFoundException();
}, 'admin');

基本用法

请求参数错误

代码语言:javascript复制
use supportRequest;
use supportResponse;
use TinywanExceptionHandlerExceptionBadRequestHttpException;

class Token{
    public function issueToken(Request $request): Response
    {
        $params = $request->post();
        if (empty($params)) {
            throw new BadRequestHttpException('账号或密码不能为空');
        }
    }
}

以上异常抛出错误信息,如下格式:

代码语言:javascript复制
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=utf-8

{
    "code": 0,
    "msg": "账号或密码不能为空",
    "data": {},
}

所有返回的异常信息将以json格式返回,以上为返回简略的异常信息

所有的异常错误处理器根据配置文件 config/app.phpdebug的值来调整错误显示, 当debug值为true (表示在调试模式), 错误处理器会显示异常以及详细的函数调用栈和源代码行数来帮助调试,将返回详细的异常信息。当debug值为false,只有错误信息会被显示以防止应用的敏感信息泄漏,将返回简略的异常信息。

返回详细的异常信息

代码语言:javascript复制
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=utf-8
        
{
    "code": 0,
    "msg": "password不允许为空",
    "data": {
        "request_url": "POST //127.0.0.1:8888/oauth/issue-token",
        "timestamp": "2022-03-06 15:19:12",
        "client_ip": "172.18.0.1",
        "request_param": {
            "username": "webman"
        },
        "error_message": "password不允许为空",
        "error_trace": "#0 /var/www/webman-admin/app/functions.php(68): Tinywan\Validate\Validate->check(Array)n#1 /var/www/webman-admin/app/controller/Authentication.php(25): ..."
    }
}

如何自定义一个自己的异常类

编写异常类

假设自定义一个:405 Method Not Allowed(表示:请求行中指定的请求方法不能被用于请求相应的资源)

自定义异常类只需要继承TinywanExceptionHandlerExceptionBaseException类即可

代码语言:javascript复制
<?php
declare(strict_types=1);

namespace supportexception;

use TinywanExceptionHandlerExceptionBaseException;

class MethodNotAllowedException extends BaseException
{
    /**
     * @var int
     */
    public $statusCode = 405;

    /**
     * @var string
     */
    public $errorMessage = '请求行中指定的请求方法不能被用于请求相应的资源';
}

使用异常类

代码语言:javascript复制
use supportRequest;
use supportResponse;
use supportexceptionMethodNotAllowedException;

class Token{
    public function issueToken(Request $request): Response
    {
        $params = $request->post();
        if (empty($params)) {
            throw new MethodNotAllowedException();
        }
    }
}

使用postman请求截图

已支持插件异常类

  • JWT 权限认证插件 异常类JwtTokenException
  • Validate 验证器插件 异常类ValidateException

内置异常类

  • 客户端异常类(HTTP Status 400):BadRequestHttpException
  • 身份认证异常类(HTTP Status 401):UnauthorizedHttpException
  • 资源授权异常类(HTTP Status 403):ForbiddenHttpException
  • 资源不存在异常类(HTTP Status 404):NotFoundHttpException
  • 路由地址不存在异常类(HTTP Status 404):RouteNotFoundException
  • 请求限流在异常类(HTTP Status 429):TooManyRequestsHttpException
  • 服务器内部错误异常类(HTTP Status 500):ServerErrorHttpException

更多:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status

自定义异常 Response

只支持修改body的三个字段的key值。必须保持顺序是:

  • ① 状态码
  • ② 错误消息
  • ③ 响应数据

使用场景

  • 每个项目有标准的统一输出,自定义返回内容
  • 前后端分离:前端要求返回的 HTTP状态码并不是 429,而是 200 或者其他
  • 响应的body不是 {"code":0,"msg":"Too Many Requests"},而是 {"error_code":200,"message":"Too Many Requests"} 等其他内容

自定义HTTP状态码

编辑 config/plugin/tinywan/exception-handler/app.php 文件的 status HTTP 状态码

自定义body返回内容

编辑 config/plugin/tinywan/exception-handler/app.php 文件的 body 的字段

「默认选项是」

代码语言:javascript复制
{
 "code": 0,
 "msg": "Too Many Requests",
 "data": null
}

自定义选项参考一

1、假设status HTTP 状态码设置为 200

2、假设body的数组设为为

代码语言:javascript复制
'body' => [
 'error_code' => 200,
 'message' => '请求太多请稍后重试'
]

则响应内容为

代码语言:javascript复制
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8

{
 "error_code": 200,
 "message": "请求太多请稍后重试"
}

其他的可以根据自身业务自定义即可

扩展自己的 Handler

当项目需要自定义情况比较多的时候,很可能需要扩展 Handler,此时可以继承 TinywanExceptionHandlerHandler 然后修改对应方法即可。

使用场景

  • response需要响应xml,而不是json格式,只需要覆盖buildResponse方法
  • 扩展其他Exception的响应,我只要覆盖solveExtraException
  • 要异常推送微信消息,我可以覆盖triggerNotifyEvent

自定义异常 ErrorHandler

代码语言:javascript复制

namespace support;

use IlluminateValidationValidationException;
use TinywanExceptionHandlerHandler;
use WebmanHttpResponse;

class ErrorHandler extends Handler
{
    /**
     * @inheritDoc
     */
    protected function solveExtraException(Throwable $e): void
    {
        // 当前项目下的异常扩展
        if ($e instanceof ValidationException) {
            $this->errorMessage = $e->validator->errors()->first();
            $this->errorCode = 422;
            return;
        }

        parent::solveExtraException($e);
    }

    /**
     * @inheritDoc
     */
    protected function triggerNotifyEvent(Throwable $e): void
    {
         // ... 这里省略触发其他错误推送渠道

        parent::triggerNotifyEvent($e);
    }

    /**
     * @inheritDoc
     */
    protected function buildResponse(): Response
    {
        // 构造自己项目下的响应
        return json([
            'code' => $this->statusCode, // 使用 statusCode 作为 code 返回
            'msg' => $this->errorMessage,
            'data' => $this->responseData,
        ]);
    }
}

异常通知(钉钉机器人)

其他

视频:https://www.bilibili.com/video/BV1bP4y1g7AL

0 人点赞