如何在 Laravel 5.1 中强制 FormRequest 返回 json?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31507849/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 11:58:49  来源:igfitidea点击:

How to force FormRequest return json in Laravel 5.1?

phplaravellaravel-5laravel-validation

提问by Chung

I'm using FormRequestto validate from which is sent in an API call from my smartphone app. So, I want FormRequest alway return json when validation fail.

我正在使用 FormRequest来验证从我的智能手机应用程序的 API 调用中发送的信息。所以,我希望 FormRequest 在验证失败时总是返回 json。

I saw the following source code of Laravel framework, the default behaviour of FormRequest is return json if reqeust is Ajax or wantJson.

我看到如下Laravel框架的源码,如果reqeust是Ajax或者wantJson,FormRequest的默认行为是return json。

//Illuminate\Foundation\Http\FormRequest class
/**
 * Get the proper failed validation response for the request.
 *
 * @param  array  $errors
 * @return \Symfony\Component\HttpFoundation\Response
 */
public function response(array $errors)
{
    if ($this->ajax() || $this->wantsJson()) {
        return new JsonResponse($errors, 422);
    }

    return $this->redirector->to($this->getRedirectUrl())
                                    ->withInput($this->except($this->dontFlash))
                                    ->withErrors($errors, $this->errorBag);
}

I knew that I can add Accept= application/jsonin request header. FormRequest will return json. But I want to provide an easier way to request my API by support json in default without setting any header. So, I tried to find some options to force FormRequest response json in Illuminate\Foundation\Http\FormRequestclass. But I didn't find any options which are supported in default.

我知道我可以添加Accept= application/json请求标头。FormRequest 将返回 json。但是我想提供一种更简单的方法来通过默认支持 json 来请求我的 API,而无需设置任何标头。所以,我试图在Illuminate\Foundation\Http\FormRequest课堂上找到一些强制 FormRequest 响应 json 的选项。但是我没有找到任何默认支持的选项。

Solution 1 : Overwrite Request Abstract Class

解决方案 1:覆盖请求抽象类

I tried to overwrite my application request abstract class like followings:

我试图覆盖我的应用程序请求抽象类,如下所示:

<?php

namespace Laravel5Cg\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\JsonResponse;

abstract class Request extends FormRequest
{
    /**
     * Force response json type when validation fails
     * @var bool
     */
    protected $forceJsonResponse = false;

    /**
     * Get the proper failed validation response for the request.
     *
     * @param  array  $errors
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function response(array $errors)
    {
        if ($this->forceJsonResponse || $this->ajax() || $this->wantsJson()) {
            return new JsonResponse($errors, 422);
        }

        return $this->redirector->to($this->getRedirectUrl())
            ->withInput($this->except($this->dontFlash))
            ->withErrors($errors, $this->errorBag);
    }
}

I added protected $forceJsonResponse = false;to setting if we need to force response json or not. And, in each FormRequest which is extends from Request abstract class. I set that option.

protected $forceJsonResponse = false;如果我们需要强制响应json,我添加了设置。并且,在从 Request 抽象类扩展的每个 FormRequest 中。我设置了那个选项。

Eg: I made an StoreBlogPostRequest and set $forceJsoResponse=truefor this FormRequest and make it response json.

例如:我创建了一个 StoreBlogPostRequest 并$forceJsoResponse=true为此 FormRequest设置并使其响应 json。

<?php

namespace Laravel5Cg\Http\Requests;

use Laravel5Cg\Http\Requests\Request;

class StoreBlogPostRequest extends Request
{

    /**
     * Force response json type when validation fails
     * @var bool
     */

     protected $forceJsonResponse = true;
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'title' => 'required|unique:posts|max:255',
            'body' => 'required',
        ];
    }
}

Solution 2: Add an Middleware and force change request header

解决方案 2:添加中间件并强制更改请求标头

I build a middleware like followings:

我构建了一个如下所示的中间件:

namespace Laravel5Cg\Http\Middleware;

use Closure;
use Symfony\Component\HttpFoundation\HeaderBag;

class AddJsonAcceptHeader
{
    /**
     * Add Json HTTP_ACCEPT header for an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $request->server->set('HTTP_ACCEPT', 'application/json');
        $request->headers = new HeaderBag($request->server->getHeaders());
        return $next($request);
    }
}

It 's work. But I wonder is this solutions good? And are there any Laravel Way to help me in this situation ?

这是工作。但我想知道这个解决方案好吗?在这种情况下,是否有任何 Laravel 方式可以帮助我?

回答by Bouke Versteegh

It boggles my mind why this is so hard to do in Laravel. In the end, based on your idea to override the Request class, I came up with this.

我很困惑为什么这在 Laravel 中很难做到。最后,根据你覆盖 Request 类的想法,我想出了这个。

app/Http/Requests/ApiRequest.php

app/Http/Requests/ApiRequest.php

<?php

namespace App\Http\Requests;


class ApiRequest extends Request
{
    public function wantsJson()
    {
        return true;
    }
}

Then, in every controller just pass \App\Http\Requests\ApiRequest

然后,在每个控制器中通过 \App\Http\Requests\ApiRequest

public function index(ApiRequest $request)

public function index(ApiRequest $request)

回答by SimonDepelchin

I know this post is kind of old but I just made a Middleware that replaces the "Accept" header of the request with "application/json". This makes the wantsJson()function return truewhen used. (This was tested in Laravel 5.2 but I think it works the same in 5.1)

我知道这篇文章有点旧,但我刚刚制作了一个中间件,用“application/json”替换了请求的“Accept”标头。这使得wantsJson()函数true在使用时返回。(这是在 Laravel 5.2 中测试过的,但我认为它在 5.1 中的工作原理相同)

Here's how you implement that :

这是你如何实现的:

  1. Create the file app/Http/Middleware/Jsonify.php

    namespace App\Http\Middleware;
    
    use Closure;
    
    class Jsonify
    {
    
        /**
         * Change the Request headers to accept "application/json" first
         * in order to make the wantsJson() function return true
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \Closure  $next
         * 
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            $request->headers->set('Accept', 'application/json');
    
            return $next($request);
        }
    }
    
  2. Add the middleware to your $routeMiddlewaretable of your app/Http/Kernel.phpfile

    protected $routeMiddleware = [
        'auth'       => \App\Http\Middleware\Authenticate::class,
        'guest'      => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'jsonify'    => \App\Http\Middleware\Jsonify::class
    ];
    
  3. Finally use it in your routes.phpas you would with any middleware. In my case it looks like this :

    Route::group(['prefix' => 'api/v1', 'middleware' => ['jsonify']], function() {
        // Routes
    });
    
  1. 创建文件 app/Http/Middleware/Jsonify.php

    namespace App\Http\Middleware;
    
    use Closure;
    
    class Jsonify
    {
    
        /**
         * Change the Request headers to accept "application/json" first
         * in order to make the wantsJson() function return true
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \Closure  $next
         * 
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            $request->headers->set('Accept', 'application/json');
    
            return $next($request);
        }
    }
    
  2. 将中间件添加到文件$routeMiddleware表中app/Http/Kernel.php

    protected $routeMiddleware = [
        'auth'       => \App\Http\Middleware\Authenticate::class,
        'guest'      => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'jsonify'    => \App\Http\Middleware\Jsonify::class
    ];
    
  3. 最后在您的routes.php任何中间件中使用它。就我而言,它看起来像这样:

    Route::group(['prefix' => 'api/v1', 'middleware' => ['jsonify']], function() {
        // Routes
    });
    

回答by Afraz Ahmad

if your request has either X-Request-With: XMLHttpRequestheader or accept content type as application/jsonFormRequest will automatically return a json response containing the errors with a status of 422.

如果您的请求具有X-Request-With: XMLHttpRequest标头或接受内容类型为 application/jsonFormRequest 将自动返回包含状态为 422 的错误的 json 响应。

enter image description here

在此处输入图片说明

回答by T30

Based on ZeroOne's response, if you're using Form Request validationyou can override the failedValidation method to always return json in case of validation failure.

根据ZeroOne 的响应,如果您使用表单请求验证,您可以覆盖 failedValidation 方法以在验证失败的情况下始终返回 json。

The good thing about this solution, is that you're not overriding all the responses to return json, but just the validation failures. So for all the other Php exceptions you'll still see the friendly Laravel error page.

这个解决方案的好处是,您不会覆盖所有返回 json 的响应,而只是覆盖验证失败。因此,对于所有其他 Php 异常,您仍然会看到友好的 Laravel 错误页面。

namespace App\Http\Requests;

use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;
use Symfony\Component\HttpFoundation\Response;

class InventoryRequest extends FormRequest
{
    protected function failedValidation(Validator $validator)
    {
        throw new HttpResponseException(response($validator->errors(), Response::HTTP_UNPROCESSABLE_ENTITY));
    }

回答by ZeroOne

i just override the failedValidationfunction

我只是覆盖了这个failedValidation功能

protected function failedValidation(Validator $validator)
{
        if ($this->wantsJson()) {
            // flatten all the message
            $collection  = collect($validator->errors())->flatten()->values()->all();
            throw new HttpResponseException(Response::error('Validation Error', $collection));
        }

        parent::failedValidation($validator);
}

So output sample:

所以输出样本:

{
    "error": true,
    "message": "Validation Error",
    "reference": [
        "The device id field is required.",
        "The os version field is required.",
        "The apps version field is required."
    ],
}