如何在Laravel中的每个响应中强制使用JSON响应?

pgpifvop  于 2023-10-21  发布在  其他
关注(0)|答案(9)|浏览(130)

我正在尝试使用Laravel Framework构建一个REST API,我想要一种方法来强制API始终使用JSON响应,而不是像这样手动执行:

return Response::json($data);

我希望每个响应都是JSON。有什么好方法可以做到这一点吗?

**更新:**响应必须是JSON,即使在异常中也是如此,例如未找到异常。

piztneat

piztneat1#

按照Alexander Lichter的建议创建一个中间件,在每个请求上设置Accept头:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class ForceJsonResponse
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        $request->headers->set('Accept', 'application/json');

        return $next($request);
    }
}

将其添加到app/Http/Kernel.php文件中的$routeMiddleware

protected $routeMiddleware = [
    (...)
    'json.response' => \App\Http\Middleware\ForceJsonResponse::class,
];

你现在可以 Package 所有应该返回JSON的路由:

Route::group(['middleware' => ['json.response']], function () { ... });

编辑:适用于Laravel 6.9+

给予json.response中间件高于其他中间件的优先级-以处理在设置Accept头之前请求被其他中间件(如Authorize中间件)终止的情况。
要做到这一点-重写App\Http\Kernel类(app/Http/Kernel.php)的构造函数:

public function __construct( Application $app, Router $router ) {
        parent::__construct( $app, $router );
        $this->prependToMiddlewarePriority(\App\Http\Middleware\ForceJsonResponse::class);
    }
mfuanj7w

mfuanj7w2#

Laravel中间件在这个用例中非常有用。

1.制作JsonResponseMiddleware中间件。

php artisan make:middleware JsonResponseMiddleware

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\ResponseFactory;

class JsonResponseMiddleware
{
    /**
     * @var ResponseFactory
     */
    protected $responseFactory;

    /**
     * JsonResponseMiddleware constructor.
     */
    public function __construct(ResponseFactory $responseFactory)
    {
        $this->responseFactory = $responseFactory;
    }

    /**
     * Handle an incoming request.
     *
     * @param Request $request
     * @param Closure $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        // First, set the header so any other middleware knows we're
        // dealing with a should-be JSON response.
        $request->headers->set('Accept', 'application/json');

        // Get the response
        $response = $next($request);

        // If the response is not strictly a JsonResponse, we make it
        if (!$response instanceof JsonResponse) {
            $response = $this->responseFactory->json(
                $response->content(),
                $response->status(),
                $response->headers->all()
            );
        }

        return $response;
    }
}

2.在App\Http\Kernel.php中注册中间件

protected $middlewareGroups = [

        'api' => [
            ...
            ....
            /// Force to Json response (Our created Middleware)
            \App\Http\Middleware\JsonResponseMiddleware::class,
        ],

        'web' => [
            ...
            ....
            /// Add Here as well if we want to force response in web routes too.
        ],
]

现在我们将只接收JSON中的每个响应。

请注意:即使异常也会以JSON格式响应

nuypyhwy

nuypyhwy3#

我知道这个问题已经得到了回答,但这些都不是好的解决方案,因为它们以不可预测的方式改变了状态代码。最好的解决方案是添加适当的头部,以便Laravel返回JSON(我认为是Accept: application/json),或者遵循这个伟大的教程总是告诉Laravel返回JSON:https://hackernoon.com/always-return-json-with-laravel-api-870c46c5efb2
如果您希望更具选择性或适应更复杂的解决方案,您也可以通过中间件来实现这一点。

dnph8jn4

dnph8jn44#

要在控制器中返回JSON,只需return $data;
要获得JSON对错误的响应,请转到app\Exceptions\Handler.php文件并查看render方法。
你应该能够重写它,看起来像这样:

public function render($request, Exception $e)
{
    // turn $e into an array.
    // this is sending status code of 500
    // get headers from $request.
    return response()->json($e, 500);
}

但是,您必须决定如何处理$e,因为它必须是array。您还可以设置状态代码和标题数组。
但是在任何错误时,它都会返回JSON响应。

  • 编辑:同样值得注意的是,您可以更改report方法来处理laravel如何记录错误。更多信息请点击此处。*
piv4azn7

piv4azn75#

我已经使用了几个混合解决方案,也在这里提到,以解决一切有点更动态。原因是总是在“/API”下面用json响应回复每个请求。
1.创建一个中间件来强制app/Http/Middleware/ForceJsonResponse.php中的JSON输出

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class ForceJsonResponse
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        // set Accept request header to application/json
        $request->headers->set('Accept', 'application/json');

        return $next($request);
    }
}

1.将此新中间件添加到app/Http/Kernel.php中API阵列的 TOP

protected $middlewareGroups = [
        ...

        'api' => [
            \App\Http\Middleware\ForceJsonResponse::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        ...
    ];

1.覆盖异常处理程序的呈现方法,所有异常也使用JSON app/Exceptions/Handler.php进行响应

namespace App\Exceptions;

  use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
+ use Throwable;

  class Handler extends ExceptionHandler
  {

     ...

+    /**
+     * Render an exception into an HTTP response.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Throwable $e
+     * @return \Illuminate\Http\Response
+     */
+    public function render($request, Throwable $e)
+    {
+        // Force to application/json rendering on API calls
+        if ($request->is('api*')) {
+            // set Accept request header to application/json
+            $request->headers->set('Accept', 'application/json');
+        }
+
+        // Default to the parent class' implementation of handler
+        return parent::render($request, $e);
+    }

}
9fkzdhlc

9fkzdhlc6#

另一个简单的解决方案是扩展Request类。使用以下命令创建app/Http/Request.php

<?php

namespace App\Http;

use Illuminate\Http\Request as BaseRequest;
use Illuminate\Support\Str;

class Request extends BaseRequest
{
    public function wantsJson(): bool
    {
        return Str::startsWith($this->path(), 'api/') || parent::wantsJson();
    }
}

然后在public/index.php中添加:

use App\Http\Request;
olqngx59

olqngx597#

您可以创建一个After Middleware并更改所有响应的结构
中间件:

namespace App\Http\Middleware;

use Closure;

class ChangeResponseStructureMiddleware
{
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        $newContent = [
            'data' => $response->getOriginalContent(),
            'context' => [
                'code' => $response->getStatusCode()
            ]
        ];

        return $response->setContent($newContent);
    }
}

该中间件将强制响应内容为

{
   "data": "response content of controller",
   "context": {
       "code": 200 // status code
   }
}
0lvr5msh

0lvr5msh8#

在Laravel 10上尝试了这个,我认为这是更干净的,无需修改/创建新的或框架文件:
在回调函数中设置请求头

Route::prefix('v1')->group(function () {
  app('request')->headers->set('Accept', 'application/json');
  Route::middleware('auth:sanctum')->group(function() {
    Route::apiResource( 'csr', V1\CsrController::class);
  });
});
yuvru6vn

yuvru6vn9#

您不应该修改请求头,因为它可能无法反映用户真正想要的内容,一般来说,这不是一个很好的做法
有一种更简单的方法可以做到这一点:重写ExceptionHandler中的shouldReturnJson()方法
默认情况下,它是return $request->expectsJson(),但您可以将其重写为(“expect json”或“from API route”)

protected function shouldReturnJson($request, Throwable $e)
{
    return $request->expectsJson() || in_array('api',$request->route()->getAction('middleware'));
}

“from API route”部分可以通过多种方式实现,你可以检查它是否在“API”中间件组中,就像我做的那样:in_array('api',$request->route()->getAction('middleware'))或检查当前URI是否以“API/”开头:Str::startsWith($request->path(), 'api/')
即使使用Authenticate中间件也是如此,因为AUthenticate中间件实际上抛出了一个Illuminate\Auth\AuthenticationException,让Handler处理
因此,通过此更改,所有错误都将以json形式从API路由返回,包括未经身份验证的错误,而无需设置Accept: application/json

相关问题