laravel-response icon indicating copy to clipboard operation
laravel-response copied to clipboard

配置API限流,请求超过限制后, 响应方式为视图

Open HelplessMan opened this issue 2 years ago • 2 comments

laravel version: 10.28.0

大致配置:

config/response.php

....

    'exception' => [
        \Illuminate\Validation\ValidationException::class => [
            'code' => 422,
        ],
        \Illuminate\Auth\AuthenticationException::class => [

        ],
        \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class =>[
            'message' => '',
        ],
        \Illuminate\Database\Eloquent\ModelNotFoundException::class => [
            'message' => '',
        ],
       // 在这里新增了一个API限流异常
        \Illuminate\Http\Exceptions\ThrottleRequestsException::class => [
            'code' => 429,
            'message' => 'Too Many Requests'
        ],
    ],

....

加了一个 Accept App\Http\Middleware\AcceptHeader.php

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class AcceptHeader
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        $request->headers->set('Accept', 'application/json');
        $request->headers->set('Content-Type', 'application/json');
        return $next($request);
    }
}


App\Http\Kernel.php

protected $middleware = [
        // \App\Http\Middleware\TrustHosts::class,
        \App\Http\Middleware\TrustProxies::class,
        \Illuminate\Http\Middleware\HandleCors::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class
    ];

    /**
     * The application's route middleware groups.
     *
     * @var array<string, array<int, class-string|string>>
     */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
           \App\Http\Middleware\AcceptHeader::class
            //\App\Http\Middleware\ListenerHttpRequest::class
        ],
    ];

然后看源码 怀疑还是 Accept没有生效?没办法,目前在App\Exceptions\Handle.php增加了一个 render方法

    public function render($request, Throwable $exception)
    {
//        if ($exception instanceof ThrottleRequestsException) {
//            return Response::fail($exception->getMessage(), 429);
//        }
         return $this->prepareJsonResponse($request, $exception);
    }

但是目前这样子的话。在访问API超过限制(1分钟5次请求) 后, 直接以浏览器方式请求的话 显示如下: image

如果是用API工具发起请求。 会出现请求阻塞。一直到超过限流时间的同时获取到数据.....

HelplessMan avatar Oct 23 '23 05:10 HelplessMan

现在好想找到原因了, 不需要 render ....

\App\Http\Middleware\AcceptHeader::class$middlewareGroups.api 的位置移动到 $middleware 这里就可以了...

这是什么原因呢?

HelplessMan avatar Oct 23 '23 11:10 HelplessMan

出现问题的原因可能是部分路由请求不符合 $request->expectsJson()

// vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php
protected function renderExceptionResponse($request, Throwable $e)
{
    return $this->shouldReturnJson($request, $e)
                ? $this->prepareJsonResponse($request, $e)
                : $this->prepareResponse($request, $e);
}

// vendor/laravel/framework/src/Illuminate/Http/Concerns/InteractsWithContentTypes.php
public function expectsJson()
{
    return ($this->ajax() && ! $this->pjax() && $this->acceptsAnyContentType()) || $this->wantsJson();
}

AcceptHeader $middleware中,相当于让所有路由请求都成了 api 请求,可以按这思路检查下

jiannei avatar Mar 14 '24 05:03 jiannei