GithubHelp home page GithubHelp logo

tinywan / webman-exception Goto Github PK

View Code? Open in Web Editor NEW
11.0 2.0 5.0 107 KB

exception handler library for webman plugin

Home Page: https://www.workerman.net/webman

License: MIT License

PHP 100.00%
webman workerman php php-library tinywan exception-handling exceptions webman-framework handler composer

webman-exception's Introduction

webman exception handler 异常插件

Latest Stable Version Total Downloads Daily Downloads License PHP Version Require exception-handler exception-handler

安装

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): validate(Array, 'app\\\\common\\\\vali...')\n#2 /var/www/webman-admin/vendor/workerman/webman-framework/src/App.php(234): app\\controller\\Authentication->issueToken(Object(support\\Request))\n#3 /var/www/webman-admin/app/middleware/AccessControlMiddleware.php(26): Webman\\App::Webman\\{closure}(Object(support\\Request))\n#4 /var/www/webman-admin/vendor/workerman/webman-framework/src/App.php(228): app\\middleware\\AccessControlMiddleware->process(Object(support\\Request), Object(Closure))\n#5 /var/www/webman-admin/vendor/workerman/webman-framework/src/App.php(137): Webman\\App::Webman\\{closure}(Object(support\\Request))\n#6 /var/www/webman-admin/vendor/workerman/workerman/Connection/TcpConnection.php(638): Webman\\App->onMessage(Object(Workerman\\Connection\\TcpConnection), Object(support\\Request))\n#7 /var/www/webman-admin/vendor/workerman/workerman/Events/Select.php(295): Workerman\\Connection\\TcpConnection->baseRead(Resource id #254)\n#8 /var/www/webman-admin/vendor/workerman/workerman/Worker.php(2417): Workerman\\Events\\Select->loop()\n#9 /var/www/webman-admin/vendor/workerman/workerman/Worker.php(1541): Workerman\\Worker->run()\n#10 /var/www/webman-admin/vendor/workerman/workerman/Worker.php(1383): Workerman\\Worker::forkOneWorkerForLinux(Object(Workerman\\Worker))\n#11 /var/www/webman-admin/vendor/workerman/workerman/Worker.php(1357): Workerman\\Worker::forkWorkersForLinux()\n#12 /var/www/webman-admin/vendor/workerman/workerman/Worker.php(549): Workerman\\Worker::forkWorkers()\n#13 /var/www/webman-admin/start.php(87): Workerman\\Worker::runAll()\n#14 {main}"
    }
}

增加异常扩展响应数据

throw new BadRequestHttpException('验证码错误',[
    'data' => [
        'data1' => '自定义响应数据1',
        'data2' => '自定义响应数据2',
    ]
]);

将会在响应的 data 增加自义定的扩展响应参数

增加data的返回详细的异常信息

{
    "code": 0,
    "msg": "验证码错误",
    "data": {
        "request_url": "POST //127.0.0.1:8888/oauth/issue-token",
         ...
        "data1": "自定义响应数据1",
        "data2": "自定义响应数据2"
    }
}

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

编写异常类

假设自定义一个: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请求截图

self-exception.png

已支持插件异常类

内置异常类

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

使用场景

  • 每个项目有标准的统一输出,自定义返回内容
  • 前后端分离:前端要求返回的 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 的字段

默认选项是

{
	"code": 0,
	"msg": "Too Many Requests",
	"data": null
}

自定义选项参考一

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

2、假设body的数组设为为

'body' => [
	'error_code' => 200,
	'message' => '请求太多请稍后重试'
]

则响应内容为

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8

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

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

扩展自己的 Handler

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

使用场景

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

自定义异常 ErrorHandler

namespace support;

use Illuminate\Validation\ValidationException;
use Tinywan\ExceptionHandler\Handler;
use Webman\Http\Response;

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,
        ]);
    }
}

异常通知(钉钉机器人)

dingtalk.png

Other

phpstan

vendor/bin/phpstan analyse src

vendor/bin/php-cs-fixer fix src

vendor/bin/php-cs-fixer fix src

webman-exception's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

webman-exception's Issues

1.3.3升级1.5.0出现异常

这是我的配置文件内容
因为我的逻辑需要用到error_code,所以更改了原来默认的code=>200为'error_code'=>200
// 自定义响应消息 'body' => [ 'error_code' => 200, 'msg' => '服务器内部异常', 'data' => null ],

抛出异常
throw new UnauthorizedHttpException('Fail 1', ['errorCode' => 3001]);

v1.3.3版本正常输出
[ "error_code": 3001, "msg": "success", "data": "data" ]

下面是V1.5.0返回的内容,error_code输出的默认值
[ "error_code": 200, "msg": "success", "data": "data" ]

composer require 报错

> post-package-install: support\Plugin::install
Script support\Plugin::install handling the post-package-install event terminated with an exception
Downloading https://packagist.org/downloads/
[201] https://packagist.org/downloads/

In ClassLoader.php line 582:
                                                                                                                                         
  [ErrorException]                                                                                                                       
  include(/mnt/projects/app-backend/vendor/tinywan/exception-handler/src/Install.php): failed to open stream: No such file or directory  
                                                                                                                                         

Exception trace:
  at phar:///usr/local/bin/composer/vendor/composer/ClassLoader.php:582
 Composer\Util\ErrorHandler::handle() at phar:///usr/local/bin/composer/vendor/composer/ClassLoader.php:582
 include() at phar:///usr/local/bin/composer/vendor/composer/ClassLoader.php:582
 Composer\Autoload\{closure}() at phar:///usr/local/bin/composer/vendor/composer/ClassLoader.php:433
 Composer\Autoload\ClassLoader->loadClass() at n/a:n/a
 spl_autoload_call() at n/a:n/a
 defined() at /mnt/projects/app-backend/vendor/workerman/webman-framework/src/support/Plugin.php:23
 support\Plugin::install() at phar:///usr/local/bin/composer/src/Composer/EventDispatcher/EventDispatcher.php:439
 Composer\EventDispatcher\EventDispatcher->executeEventPhpScript() at phar:///usr/local/bin/composer/src/Composer/EventDispatcher/EventDispatcher.php:257
 Composer\EventDispatcher\EventDispatcher->doDispatch() at phar:///usr/local/bin/composer/src/Composer/EventDispatcher/EventDispatcher.php:148
 Composer\EventDispatcher\EventDispatcher->dispatchPackageEvent() at phar:///usr/local/bin/composer/src/Composer/Installer/InstallationManager.php:396
 Composer\Installer\InstallationManager::Composer\Installer\{closure}() at phar:///usr/local/bin/composer/src/Composer/Installer/InstallationManager.php:411
 Composer\Installer\InstallationManager->executeBatch() at phar:///usr/local/bin/composer/src/Composer/Installer/InstallationManager.php:319
 Composer\Installer\InstallationManager->downloadAndExecuteBatch() at phar:///usr/local/bin/composer/src/Composer/Installer/InstallationManager.php:218
 Composer\Installer\InstallationManager->execute() at phar:///usr/local/bin/composer/src/Composer/Installer.php:797
 Composer\Installer->doInstall() at phar:///usr/local/bin/composer/src/Composer/Installer.php:622
 Composer\Installer->doUpdate() at phar:///usr/local/bin/composer/src/Composer/Installer.php:287
 Composer\Installer->run() at phar:///usr/local/bin/composer/src/Composer/Command/RequireCommand.php:490
 Composer\Command\RequireCommand->doUpdate() at phar:///usr/local/bin/composer/src/Composer/Command/RequireCommand.php:339
 Composer\Command\RequireCommand->execute() at phar:///usr/local/bin/composer/vendor/symfony/console/Command/Command.php:298
 Symfony\Component\Console\Command\Command->run() at phar:///usr/local/bin/composer/vendor/symfony/console/Application.php:1040
 Symfony\Component\Console\Application->doRunCommand() at phar:///usr/local/bin/composer/vendor/symfony/console/Application.php:301
 Symfony\Component\Console\Application->doRun() at phar:///usr/local/bin/composer/src/Composer/Console/Application.php:378
 Composer\Console\Application->doRun() at phar:///usr/local/bin/composer/vendor/symfony/console/Application.php:171
 Symfony\Component\Console\Application->run() at phar:///usr/local/bin/composer/src/Composer/Console/Application.php:141
 Composer\Console\Application->run() at phar:///usr/local/bin/composer/bin/composer:88
 require() at /usr/local/bin/composer:29

require [--dev] [--dry-run] [--prefer-source] [--prefer-dist] [--prefer-install PREFER-INSTALL] [--fixed] [--no-suggest] [--no-progress] [--no-update] [--no-install] [--no-audit] [--audit-format AUDIT-FORMAT] [--update-no-dev] [-w|--update-with-dependencies] [-W|--update-with-all-dependencies] [--with-dependencies] [--with-all-dependencies] [--ignore-platform-req IGNORE-PLATFORM-REQ] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [--sort-packages] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--apcu-autoloader-prefix APCU-AUTOLOADER-PREFIX] [--] [<packages>...]

走到安装插件的这一步就报错了,虽然再安装一次就不报错了,但是运行起来会有问题,config方法获取不了配置了。

event 问题

app.php 下的 event 与 webman/event 存在冲突

image

按文档无法正确得到预期异常: BadRequestHttpException, MethodNotAllowedException

如题.版本 krisss/docker-webman:8.0-cli-alpine
需要按文档重写handler方法
protected function solveAllException(Throwable $e)
{
if ($e instanceof BaseException) {
$this->statusCode = $e->statusCode;
$this->header = $e->header;
$this->errorCode = $e->errorCode;
$this->errorMessage = $e->errorMessage;
$this->error = $e->error;
if (isset($e->data)) {
$this->responseData = array_merge($this->responseData, $e->data);
}
return ; // 在这里直接返回,否则会被下面的方法接管结果.导致500,Internal Server Error
}
$this->solveExtraException($e);
}

自定义body没生效

问题描述
Exception 异常插件,自定义body没生效,不知道是不是使用方式不对
image

根据插件文档配置
image

配置代码
image

接口返回
image

扩展自己的 Handler

使用文档中的方法,拓展自己的Handler,并不能生效。请问还需要其他配置吗?

namespace support;

use hg\apidoc\exception\HttpException;
use Tinywan\ExceptionHandler\Handler;

class ErrorHandler extends Handler
{
/**
* @inheritdoc
*/
protected function solveExtraException(\Throwable $e): void
{
// 当前项目下的异常扩展
if ($e instanceof HttpException) {
$this->errorMessage = 'Apidoc异常';
$this->statusCode = 401;
return;
}
parent::solveExtraException($e);
}
}

utf8_encode php8.2版本弃用了。抛出钉钉时候报错

首先很感谢作者的付出。插件很好用。
使用的问题请留意。一个是格式函数弃用,另外一个dingtalk key多了下划线代码对应不上。

class DingTalkRobotEvent

private static function _sign(string $url, string $secret): string
{
[$s1, $s2] = explode(' ', microtime());
$timestamp = (float)sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
$data = $timestamp . "\n" . $secret;
$signStr = base64_encode(hash_hmac('sha256', $data, $secret, true));
#这部分做的兼容
if(PHP_VERSION_ID < 82000){
$signStr = mb_convert_encoding($signStr, 'UTF-8', 'ISO-8859-1');
} else {
$signStr = utf8_encode(urlencode($signStr));
}
$signStr = mb_convert_encoding(urlencode($signStr), 'UTF-8', 'ISO-8859-1');
return $url . "&timestamp=$timestamp&sign=$signStr";
}

您好我这边安装后 这个报错

Webman\Exception\NotFoundException: Class 'Tinywan\ExceptionHandler\Handler' not found in /www/wwwroot/cg.nodebug.cn/vendor/workerman/webman-framework/src/Container.php:68 Stack trace: #0 /www/wwwroot/cg.nodebug.cn/vendor/workerman/webman-framework/src/App.php(259): Webman\Container->make() #1 /www/wwwroot/cg.nodebug.cn/vendor/workerman/webman-framework/src/App.php(354): Webman\App::exceptionResponse() #2 /www/wwwroot/cg.nodebug.cn/support/middleware/CrossMiddleware.php(13): Webman\App::Webman{closure}() #3 /www/wwwroot/cg.nodebug.cn/vendor/workerman/webman-framework/src/App.php(341): support\middleware\CrossMiddleware->process() #4 /www/wwwroot/cg.nodebug.cn/vendor/workerman/webman-framework/src/App.php(141): Webman\App::Webman{closure}() #5 /www/wwwroot/cg.nodebug.cn/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage() #6 /www/wwwroot/cg.nodebug.cn/vendor/workerman/workerman/Events/Select.php(311): Workerman\Connection\TcpConnection->baseRead() #7 /www/wwwroot/cg.nodebug.cn/vendor/workerman/workerman/Worker.php(1629): Workerman\Events\Select->loop() #8 /www/wwwroot/cg.nodebug.cn/vendor/workerman/workerman/Worker.php(1423): Workerman\Worker::forkOneWorkerForLinux() #9 /www/wwwroot/cg.nodebug.cn/vendor/workerman/workerman/Worker.php(1397): Workerman\Worker::forkWorkersForLinux() #10 /www/wwwroot/cg.nodebug.cn/vendor/workerman/workerman/Worker.php(560): Workerman\Worker::forkWorkers() #11 /www/wwwroot/cg.nodebug.cn/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll() #12 /www/wwwroot/cg.nodebug.cn/start.php(4): support\App::run() #13 {main}

BaseException 适配一下getMessage()

在BaseException类中

public function __construct(string $errorMessage = '', array $params = [])
    {
        parent::__construct();
        if (!empty($errorMessage)) {
            $this->errorMessage = $errorMessage;
            $this->message=$errorMessage;//加一行这个可好
        }
        if (!empty($params)) {
            if (array_key_exists('statusCode', $params)) {
                $this->statusCode = $params['statusCode'];
            }
            if (array_key_exists('header', $params)) {
                $this->header = $params['header'];
            }
            if (array_key_exists('errorCode', $params)) {
                $this->errorCode = $params['errorCode'];
            }
            if (array_key_exists('data', $params)) {
                $this->data = $params['data'];
            }
        }
    }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.