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

场景描述

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

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

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

1X-RateLimit-Limit # 时间范围内可请求次数 2X-RateLimit-Remaining # 时间范围内剩余可请求次数 3X-RateLimit-Reset # 到期时间戳

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

1{ 2 "message": "You have exceeded your rate limit.", 3 "result": 0, 4 "status_code": 429 5}

异常接管

这里有两种接管方式

  1. 单一异常接管:

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

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

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

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

    1<?php 2 3namespace App\Exceptions; 4 5use Dingo\Api\Contract\Debug\ExceptionHandler; 6use Dingo\Api\Exception\Handler as DingoHandler; 7use Dingo\Api\Exception\RateLimitExceededException; 8use Exception; 9 10class DingoExceptionHandler extends DingoHandler implements ExceptionHandler { 11 12 public function handle(Exception $exception) { 13 if ($exception instanceof RateLimitExceededException) { 14 return response([ 15 'message' => '当前请求太过频繁', 16 'result' => 0, 17 'status_code' => 429 18 ])->withHeaders($exception->getHeaders()); 19 } 20 21 // TODO: 此处可对其它异常进行同样方式的处理 22 23 return parent::handle($exception); 24 } 25}

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

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

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

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

    1{ 2 "message": "当前请求太过频繁", 3 "result": 0, 4 "status_code": 429 5}

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


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

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

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

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

1<?php 2 3namespace App\Providers; 4 5use App\Exceptions\DingoExceptionHandler; 6use App\Http\DingoAPI\StrictHeaderAccept; 7use Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider; 8use Dingo\Api\Http\Validation\Accept; 9use Dingo\Api\Provider\LumenServiceProvider as DingoAPI; 10use Illuminate\Redis\RedisServiceProvider; 11use Illuminate\Support\ServiceProvider; 12 13class AppServiceProvider extends ServiceProvider { 14 15 /** 16 * Register any application services. 17 * 18 * @return void 19 */ 20 public function register() { 21 // Dingo API 22 $this->app->register(DingoAPI::class); 23 24 // Overwrite request header Accept verify 25 $this->app->singleton(Accept::class, function () { // Dingo API Accept 严格头的简易白名单方式,StrictHeaderAccept 类参考下方 26 return new Accept(new StrictHeaderAccept( 27 config('api.standardsTree'), 28 config('api.subtype'), 29 config('api.version'), 30 config('api.defaultFormat')), 31 config('api.strict')); 32 }); 33 34 // Overwrite rate limit exception render 35 $this->app->singleton('api.exception', function () { 36 return new DingoExceptionHandler( 37 $this->app['Illuminate\Contracts\Debug\ExceptionHandler'], 38 config('api.errorFormat'), 39 config('api.debug') 40 ); 41 }); 42 43 // Redis 44 $this->app->register(RedisServiceProvider::class); 45 46 // IDE Helper 47 if ($this->app->environment() !== 'production') { 48 $this->app->register(IdeHelperServiceProvider::class); 49 } 50 } 51}

StrictHeaderAccept.php 内容:

1<?php 2 3namespace App\Http\DingoAPI; 4 5use Dingo\Api\Http\Parser\Accept; 6use Illuminate\Http\Request; 7 8class StrictHeaderAccept extends Accept { 9 10 public function parse(Request $request, $strict = false) { 11 if (in_array($request->getPathInfo(), config('whitelist.request.header'))) { 12 $strict = false; 13 } 14 15 return parent::parse($request, $strict); 16 } 17}
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )