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

Php 如何在Laravel 5.1中强制FormRequest返回json?,php,laravel,laravel-5,laravel-validation,Php,Laravel,Laravel 5,Laravel Validation,我正在使用它来验证智能手机应用程序在API调用中发送的消息。因此,我希望FormRequest在验证失败时始终返回json 我看到了以下Laravel框架的源代码,如果Request是Ajax或wantJson,FormRequest的默认行为是返回json //Illuminate\Foundation\Http\FormRequest class /** * Get the proper failed validation response for the request. * * @

我正在使用它来验证智能手机应用程序在API调用中发送的消息。因此,我希望FormRequest在验证失败时始终返回json

我看到了以下Laravel框架的源代码,如果Request是Ajax或wantJson,FormRequest的默认行为是返回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);
}
我知道我可以在请求头中添加
Accept=application/json
。FormRequest将返回json。但是我想提供一种更简单的方法来请求我的API,默认情况下支持json,而不设置任何头。因此,我尝试在
illighted\Foundation\Http\FormRequest
类中找到一些选项来强制FormRequest-response-json。但是我没有找到默认支持的任何选项

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

<?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);
    }
}
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);
    }
}
解决方案2:添加中间件和强制更改请求头 我构建了如下中间件:
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);
    }
}

这是工作。但我想知道这个解决方案好吗?在这种情况下,有没有办法帮助我

这让我很困惑,为什么在拉威尔这么难做到。最后,基于您重写请求类的想法,我提出了这个

app/Http/Requests/apirest.php

<?php

namespace App\Http\Requests;


class ApiRequest extends Request
{
    public function wantsJson()
    {
        return true;
    }
}
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);
    }
}

我知道这篇文章有点陈旧,但我只是制作了一个中间件,用“application/json”替换请求的“Accept”头。这使得
wantsJson()
函数在使用时返回
true
。(这在Laravel 5.2中进行了测试,但我认为其在5.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);
        }
    }
    
  • 将中间件添加到
    app/Http/Kernel.php
    文件的
    $routeMiddleware
    表中

    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);
        }
    }
    
    protected $routeMiddleware = [
        'auth'       => \App\Http\Middleware\Authenticate::class,
        'guest'      => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'jsonify'    => \App\Http\Middleware\Jsonify::class
    ];
    
  • 最后,在
    routes.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);
        }
    }
    
    Route::group(['prefix' => 'api/v1', 'middleware' => ['jsonify']], function() {
        // Routes
    });
    

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


    我只是覆盖了
    失败验证
    功能

    protected function failedValidation(Validator $validator)
    {
        if ($this->wantsJson()) {
            throw new HttpResponseException(
                Response::error(__('api.validation_error'), 
                $validator->errors(), 
                470, 
                [], 
                new ValidationException)
            );
        }
    
        parent::failedValidation($validator);
    }
    
    因此,我的自定义输出示例如下所示:

    {
        "error": true,
        "message": "Validation Error",
        "reference": [
            "The device id field is required.",
            "The os version field is required.",
            "The apps version field is required."
        ],
    }
    
    顺便说一句:laravel中不存在错误。Im使用MacroTable创建新方法

     Response::macro('error', function ($msg = 'Something went wrong', $reference = null, $code = 400, array $headers = [], $exception = null) {
          return response()->json(//custom here);
     });
    
    基于,如果您使用的是failedValidation方法,那么在验证失败的情况下,您可以重写failedValidation方法以始终返回json

    这个解决方案的好处在于,您不会覆盖所有返回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));
        }
    }
    


    您好,我只是想提出一个建议,既然您要求默认响应类型,那么为什么不在您的handle方法
    $request->header('accept','application/json')中添加一个中间件并将请求类型添加到json中呢;返回$next($request)有了这些,您就有了进一步扩展的空间,而不必总是覆盖任何MethodsHanks!这是个好主意。我想。我将在上面的问题中更新此实现。我试图设置$request->header('Accept','application/json');在中间件中,但我发现我的请求具有默认的Accept标头“/”,因此我无法覆盖该Accept标头。我没有在我的请求中设置任何内容。这无关紧要,不管您是否在头中设置了默认的accept值,中间件值都将覆盖它
    $request=$request->header('accept','application/json');返回$next($request)我认为,请求没有被持久化。我找到了覆盖请求头的方法,我们需要设置$request->server并重建headerBag,如下所示:$request->server->set('HTTP_ACCEPT',application/json')$request->headers=newheaderbag($request->server->getHeaders());我也有同样的问题,我想试试这个解决方案。我遇到的问题是,当我的控制器方法隐式加载我的FormRequest实例时,它与Jsonify中间件中修改的请求实例不同,因此WANTSSON实际上被“重置”为false。FormRequest扩展了请求,因此它应该是同一个实例,也许给我看看code@SimonDepelchin这个解决方案类似于我在问题中提到的解决方案2。是的,它有更多的细节和更多的“Laravel方式”imho。这个解决方案更好,因为它以JSON的形式返回所有内容。如果您使用
    apirest
    发出未经授权的请求,它将返回404 html页面,但这将返回401个未经授权的JSON错误。接受:标题中的应用程序/JSON也可能有助于解决此问题。我在测试5.4应用程序时遇到此问题。简单地将这个方法添加到a中就可以做到这一点。“这让我觉得有多难”…“这是一个非常简单的方法”,哈哈,这让我笑了+1它不工作:这样的请求为空
    $request->all()
    这在工作或不工作的二进制意义上工作,但是考虑到这个问题起草得有多好,这是一个真正的解决方案。你能分享
    Response
    class@Manojkiran.AIllumb\Support\Facades\Response.Clear解决方案,非常感谢,在Laravel 6.14上运行良好
    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);
        }
    }