【问题标题】:How do you force a JSON response on every response in Laravel?你如何在 Laravel 中的每个响应上强制一个 JSON 响应?
【发布时间】:2016-07-21 20:58:36
【问题描述】:

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

return Response::json($data);

换句话说,我希望每个响应都是 JSON。有什么好办法吗?

更新:响应必须是 JSON 格式,即使出现像未找到这样的异常 例外。

【问题讨论】:

  • 那么您想要的是能够编写return $data 并将其翻译成return Response::json($data);
  • 为此使用中间件。创建一个可终止的,然后让它返回你想要的。

标签: php json rest laravel


【解决方案1】:

要在控制器中返回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 记录错误的方式。更多信息here

【讨论】:

  • 这将返回行 xdeubg 消息,我需要有 laravel 调试响应
  • @MustafaDwekat,我猜它最终工作了?你需要改变我的回答吗?如果是这样,请告诉我,我会更新它。
  • 在那里玩了一段时间后,我实际上可以从中得到我想要的任何东西。非常感谢。这很有帮助。
  • Puncher 太好了,我想念它,但我想补充一点,您需要在返回之前形成错误响应,如 $response = ['error_code'=> $e->getStatusCode() , 'trace'=>$e->getTrace()] 然后返回。
  • @MustafaDwekat,啊,是的。在传递之前,您必须将 Exception 转换为数组。很高兴它成功了。查看docs on the exception handler 以获取有关异常处理程序可以做什么的更多信息。如果您需要以某种方式记录这些错误,您也可以通过修改report 方法来控制它。干杯!
【解决方案2】:

我知道这个问题已经得到解答,但这些都不是好的解决方案,因为它们会以不可预知的方式更改状态代码。最好的解决方案是添加适当的标头以便 Laravel 返回 JSON(我认为它的 Accept: application/json),或者按照这个很棒的教程总是告诉 Laravel 返回 JSON:https://hackernoon.com/always-return-json-with-laravel-api-870c46c5efb2

如果您想更有选择性或适应更复杂的解决方案,您也可以通过中间件来做到这一点。

【讨论】:

  • 我认为这是解决问题的最佳方案。
  • @engma 不受影响。 FormRequest 与请求验证/授权有关。问题/答案与响应有关。
【解决方案3】:

按照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,
];

您现在可以包装所有应该返回 JSON 的路由:

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

编辑:对于 Laravel 6.9+

json.response 中间件优先于其他中间件 - 以处理请求被其他中间件(例如Authorize 中间件)终止的情况,然后再设置Accept 标头。

为此 - 使用以下命令覆盖 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);
    }

【讨论】:

  • 这个方法最干净!我们也可以在客户端添加Accept: application/json,但这样更一致
  • 你还需要给这个中间件优先级,以防其他中间件在该中间件采取行动之前终止了请求。为此,您应该将$this-&gt;prependToMiddlewarePriority(\App\Http\Middleware\ForceJsonResponse::class); 添加到Http\Kernel 类的构造函数中
  • 用优先事项编辑了答案 - 这绝对应该是正确的答案
  • 我最好在 Laravel 8+ 中将规则添加到组中。在我的例子中是 API 中间件组。 public function __construct( Application $app, Router $router ) { parent::__construct( $app, $router ); $this-&gt;prependMiddlewareToGroup('api',\App\Http\Middleware\ForceJsonResponse::class); }
【解决方案4】:

您可以创建 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
   }
}

【讨论】:

    【解决方案5】:

    我使用了这里提到的几种混合解决方案来解决所有问题。原因是始终使用 json 响应来回复“/api”下方的每个请求。

    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);
    +    }
    
    }
    

    【讨论】:

      【解决方案6】:

      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 格式响应

      【讨论】:

      • 使用中间件比从构造函数返回响应更好。谢谢亲爱的。您的解决方案对我有用。
      【解决方案7】:

      另一个简单的解决方案是扩展 Request 类:

      <?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;
      

      【讨论】:

        猜你喜欢
        • 2018-09-15
        • 1970-01-01
        • 2021-07-22
        • 2017-12-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-16
        相关资源
        最近更新 更多