🚀 我的第四个webman插件 exception-handler 异常插件

Tinywan

简介

插件地址:https://www.workerman.net/plugin/16

Total Downloads

安装

composer require tinywan/exception-handler

使用

配置

config/exception.php

return [
    // 这里配置异常处理类
    '' => \Tinywan\ExceptionHandler\Handler::class,
];

多应用模式时,你可以为每个应用单独配置异常处理类,参见多应用

基本用法

请求参数错误

use support\Request;
use support\Response;
use Tinywan\ExceptionHandler\Exception\BadRequestHttpException;

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

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

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,只有错误信息会被显示以防止应用的敏感信息泄漏,将返回简略的异常信息。

返回详细的异常信息

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(表示:请求行中指定的请求方法不能被用于请求相应的资源)

自定义异常类只需要继承Tinywan\ExceptionHandler\Exception\BaseException类即可

<?php
declare(strict_types=1);

namespace support\exception;

use Tinywan\ExceptionHandler\Exception\BaseException;

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

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

使用自定义

use support\Request;
use support\Response;
use support\exception\MethodNotAllowedException;

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

使用postman请求截图

已支持插件异常类

内置异常类

  • 客户端异常类(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

效果图(可选)

3229 9 1
9个评论

coder_1024

src/Handler.php文件90行和97行,如果读不到配置会报错,建议加上默认空数组值。

  • Tinywan 2022-05-12

    卸载重新安装就会覆盖掉老版本的配置,你这个应该是配置文件是很早的版本,插件是最新版本导致的问题

sayal

本来在中间件里设置了跨源处理,这个异常出错了就无法返回信息了,这个要怎么处理?

  • sayal 2022-10-10

    已经解决了,重新建了一个类,继承Tinywan\ExceptionHandler\Handler,重写了buildResponse方法

  • Tinywan 2022-10-10

深蓝

BadRequestHttpException 返回的code状态码如何更改呢?

  • 暂无评论
Tinywan

BadRequestHttpException 返回的code状态码如何更改呢?

throw new BadRequestHttpException('客户端请求错误', ['errorCode' => 200]);

希望返回的code状态码为 200

  • 深蓝 2023-03-31

    感谢回复,昨天我已经搞明白了。

编程小白

"Server Unknown Error" 应该怎么处理这种保存呢
他没有显示报错的具体内容。

hon陈烁临

很棒的插件,大佬 Handler::class 可以加一个可选项,异常上报Sentry

晚安。

截图
这些额外的信息不需要返回给客户端,怎么去掉呢

  • Tinywan 2023-08-25

    返回会有什么问题吗?这主要是一些请求参数

  • 软饭工程师 2023-08-25

    将 buildResponse 返回的data 值给于空值就好了

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

    噢噢

晚安。

还有一个调试模式下
截图
这个怎么不会显示实际的错误信息

  • Tinywan 2023-08-25

    调试模式下就在详细的调试日志

zhaogh

在同时安装illuminate/events和webman/log的情况下,抛出异常会被webman/log记录为SQL异常日志,实际写入日志是在webman/log插件里写入

// config/exception.php
return [
    // '' => support\ErrorHandler::class,
    '' => \Tinywan\ExceptionHandler\Handler::class,
];

// config/log.php
return [
    'default' => [
        'handlers' => [
            [
                'class' => Monolog\Handler\RotatingFileHandler::class,
                'constructor' => [
                    runtime_path() . '/logs/webman.log',
                    7, //$maxFiles
                    env('APP_DEBUG', false)?Monolog\Logger::DEBUG : Monolog\Logger::WARNING,
                ],
                'formatter' => [
                    'class' => Monolog\Formatter\LineFormatter::class,
                    'constructor' => [null, 'Y-m-d H:i:s', true],
                ],
            ],
        ],
    ],
];

// 中间件
namespace app\middleware;
class LoginAuth implements MiddlewareInterface
{
    public function process(Request $request, callable $next): Response
    {
        $user_id = JwtToken::getCurrentId();
        if ($user_id === 0) {
            throw new UnauthorizedHttpException('ID异常');
        }

        $user = LyUser::where('user_id', $user_id)->first([
            'user_id',
            'state',
        ]);
        if (!$user) {
            throw new UnauthorizedHttpException('用户不存在');
        }

        if ($user->state == LyUserStateConstant::LOCK) {
            throw new UnauthorizedHttpException('用户被锁定');
        }

        return $next($request);
    }
}

日志信息如下

[2024-01-22 19:19:21] default.ERROR: xxx.xx.xxx.xx GET xxxxx.com.cn/talk/lists [10.6711ms] [webman/log]
[SQL]   [connection:default] select * from `tb_user` where `tb_user`.`user_id` = 1003190 limit 1 [4.62 ms]
Tinywan\ExceptionHandler\Exception\UnauthorizedHttpException in /data1/services/incoming/app/middleware/LoginAuth.php:45
Stack trace:
#0 /data1/services/incoming/vendor/workerman/webman-framework/src/App.php(341): app\middleware\LoginAuth->process(Object(support\Request), Object(Closure))
#1 /data1/services/incoming/vendor/webman/log/src/Middleware.php(58): Webman\App::Webman\{closure}(Object(support\Request))
#2 /data1/services/incoming/vendor/workerman/webman-framework/src/App.php(341): Webman\Log\Middleware->process(Object(support\Request), Object(Closure))
#3 /data1/services/incoming/vendor/webman/cors/src/CORS.php(12): Webman\App::Webman\{closure}(Object(support\Request))
#4 /data1/services/incoming/vendor/workerman/webman-framework/src/App.php(341): Webman\Cors\CORS->process(Object(support\Request), Object(Closure))
#5 /data1/services/incoming/app/middleware/GlobalLog.php(25): Webman\App::Webman\{closure}(Object(support\Request))
#6 /data1/services/incoming/vendor/workerman/webman-framework/src/App.php(341): app\middleware\GlobalLog->process(Object(support\Request), Object(Closure))
#7 /data1/services/incoming/vendor/workerman/webman-framework/src/App.php(141): Webman\App::Webman\{closure}(Object(support\Request))
#8 /data1/services/incoming/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage(Object(Workerman\Connection\TcpConnection), Object(support\Request))
#9 [internal function]: Workerman\Connection\TcpConnection->baseRead(Resource id #14199, 2, Resource id #14199)
#10 /data1/services/incoming/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#11 /data1/services/incoming/vendor/workerman/workerman/Worker.php(1635): Workerman\Events\Event->loop()
#12 /data1/services/incoming/vendor/workerman/workerman/Worker.php(1426): Workerman\Worker::forkOneWorkerForLinux(Object(Workerman\Worker))
#13 /data1/services/incoming/vendor/workerman/workerman/Worker.php(1400): Workerman\Worker::forkWorkersForLinux()
#14 /data1/services/incoming/vendor/workerman/workerman/Worker.php(560): Workerman\Worker::forkWorkers()
#15 /data1/services/incoming/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#16 /data1/services/incoming/start.php(4): support\App::run()
#17 {main}
 [] []

目前处理方式是

config/plugin/webman/log/app.php
return [
    'enable' => true,
    'exception' => [
        // 是否记录异常到日志
        'enable' => true,
        // 不会记录到日志的异常类
        'dontReport' => [
            support\exception\BusinessException::class,
            Tinywan\Validate\Exception\ValidateException::class,
            Tinywan\ExceptionHandler\Exception\UnauthorizedHttpException::class,
        ],
    ],
];
年代过于久远,无法发表评论

Tinywan

11950
积分
0
获赞数
0
粉丝数
2020-01-14 加入
🔝