Lumen 中对 Dingo API 异常接管并自定义响应结果

Lumen 中对 Dingo API 异常接管并自定义响应结果场景描述 比如我们需要对 API 限流抛出的异常进行接管 并重写响应消息 首先应用中间件 use Dingo Api Routing Router api gt group middleware gt api throttle

大家好,我是讯享网,很高兴认识大家。

场景描述

比如我们需要对 API 限流抛出的异常进行接管,并重写响应消息,首先应用中间件:

use Dingo\Api\Routing\Router; $api->group([ 'middleware' => 'api.throttle', // 限流中间件 'expires' => 1, // 时间范围,单位“分” 'limit' => 2, // 时间范围内请求次数 ], function (Router $api) { 
    $api->post('auth/login', 'LoginController@login'); }); 

讯享网

使用 Postman 进行接口调试,我们会发现在正常请求阶段会多出三个响应头:

讯享网X-RateLimit-Limit # 时间范围内可请求次数 X-RateLimit-Remaining # 时间范围内剩余可请求次数 X-RateLimit-Reset # 到期时间戳 

继续重复请求两次后会得到类似如下结果(修改过):

{ 
    "message": "You have exceeded your rate limit.", "result": 0, "status_code": 429 } 

异常接管

这里有两种接管方式

  1. 单一异常接管:

    建议在 App\Providers\AppServiceProvider 文件中的 register() 方法内进行编写:

    讯享网$this->app->make(Dingo\Api\Exception\Handler::class)->register(function (RateLimitExceededException $e) { 
          return response([ 'message' => '当前请求太过频繁', 'result' => 0, 'status_code' => 429 ])->setStatusCode($e->getStatusCode())->withHeaders($e->getHeaders()); }); 
     如果无需使用状态码,可去掉 setStatusCode 方法,仅保留 withHeaders 即可。去掉后 HTTP 状态码响应为 200。 
  2. 多异常接管:

    顾名思义,单一异常接管仅适用于单一的服务场景,而 Dingo API 提供了多项服务,如果应用多项时,上述方式就不适用了。

    首先,我们在 app/Exceptions 目录内创建名为 DingoExceptionHandler 的类文件,同样我们以限流异常示例,内容如下:


    讯享网

    讯享网<?php namespace App\Exceptions; use Dingo\Api\Contract\Debug\ExceptionHandler; use Dingo\Api\Exception\Handler as DingoHandler; use Dingo\Api\Exception\RateLimitExceededException; use Exception; class DingoExceptionHandler extends DingoHandler implements ExceptionHandler { 
          public function handle(Exception $exception) { 
          if ($exception instanceof RateLimitExceededException) { 
          return response([ 'message' => '当前请求太过频繁', 'result' => 0, 'status_code' => 429 ])->withHeaders($exception->getHeaders()); } // TODO: 此处可对其它异常进行同样方式的处理 return parent::handle($exception); } } 

    上方类文件还未应用,此时应当将其注入到框架容器中。

    打开 App\Providers\AppServiceProvider 文件,在 register() 方法中添加:

    $this->app->singleton('api.exception', function () { 
          return new App\Exceptions\DingoExceptionHandler( $this->app['Illuminate\Contracts\Debug\ExceptionHandler'], config('api.errorFormat'), config('api.debug') ); }); 

    至此接管完成,再次进行请求测试,响应结果变更为:

    讯享网{ 
          "message": "当前请求太过频繁", "result": 0, "status_code": 429 } 

    响应头中会多出一项 Retry-After,值为剩余可请求时间,单位秒,即 n 秒后允许请求。


说句题外话,之前看到网上很多人在 Lumen 框架中对服务的注册都是通过 $app->register() 写在 bootstrap/app.php 文件内,

个人建议不要这么做,应当统一写在 app\Providers\AppServiceProvider.php 文件内,因为在 app.php 中人家已经注册了这玩意儿

$app->register(App\Providers\AppServiceProvider::class); 

那何不规范的写在 Providers 里面呢?例如:

讯享网<?php namespace App\Providers; use App\Exceptions\DingoExceptionHandler; use App\Http\DingoAPI\StrictHeaderAccept; use Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider; use Dingo\Api\Http\Validation\Accept; use Dingo\Api\Provider\LumenServiceProvider as DingoAPI; use Illuminate\Redis\RedisServiceProvider; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { 
    / * Register any application services. * * @return void */ public function register() { 
    // Dingo API $this->app->register(DingoAPI::class); // Overwrite request header Accept verify $this->app->singleton(Accept::class, function () { 
    // Dingo API Accept 严格头的简易白名单方式,StrictHeaderAccept 类参考下方 return new Accept(new StrictHeaderAccept( config('api.standardsTree'), config('api.subtype'), config('api.version'), config('api.defaultFormat')), config('api.strict')); }); // Overwrite rate limit exception render $this->app->singleton('api.exception', function () { 
    return new DingoExceptionHandler( $this->app['Illuminate\Contracts\Debug\ExceptionHandler'], config('api.errorFormat'), config('api.debug') ); }); // Redis $this->app->register(RedisServiceProvider::class); // IDE Helper if ($this->app->environment() !== 'production') { 
    $this->app->register(IdeHelperServiceProvider::class); } } } 

StrictHeaderAccept.php 内容:

<?php namespace App\Http\DingoAPI; use Dingo\Api\Http\Parser\Accept; use Illuminate\Http\Request; class StrictHeaderAccept extends Accept { 
    public function parse(Request $request, $strict = false) { 
    if (in_array($request->getPathInfo(), config('whitelist.request.header'))) { 
    $strict = false; } return parent::parse($request, $strict); } } 
小讯
上一篇 2025-03-03 09:16
下一篇 2025-03-02 23:11

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/49043.html