【问题标题】:Disable request validation redirect in Laravel 5.4在 Laravel 5.4 中禁用请求验证重定向
【发布时间】:2018-03-03 04:22:54
【问题描述】:

所以我正在尝试为一个内部项目开发一个 rest API,我遇到了一个问题,当表单请求验证失败时,它会显示 @index 响应。

所以我有两条路线;

Route::get('/api/clients', 'ClientController@index');
Route::post('/api/clients', 'ClientController@store');

@index 列出所有客户端,@store 创建一个新客户端,我在@store 方法上有一个表单请求验证器,用于检查是否为客户端提供了名称。

我想要的是当验证器失败时,它会显示一个带有验证错误的 JSON 响应。但我认为它发生的情况是验证失败,因此它重定向回同一页面,但重定向是GET 而不是POST,因此它列出了所有客户端。

我知道您可以设置您的标头,使其看起来像一个 ajax 请求,其中它将正确显示 JSON 响应,但我希望它显示 JSON 响应,无论它是否是 ajax。

我尝试在我的验证器中覆盖 response 方法但不起作用,我尝试将验证器中的 wantsJson 方法设置为返回 true 再次不起作用。

非常感谢您的帮助。

代码如下...

web.php

Route::get('/api/clients', 'ClientController@index');
Route::get('/api/clients/{client}', 'ClientController@show');
Route::post('/api/clients', 'ClientController@store');
Route::put('/api/clients/{id}', 'ClientController@update');
Route::delete('/api/clients/{id}', 'ClientController@delete');

ClientController.php

namespace App\Http\Controllers;

use App\Client;
use App\Http\Requests\ClientRequest;

class ClientController extends Controller
{

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(ClientRequest $request)
    {
        return Client::create([
            'title'   => request('title'),
            'user_id' => auth()->id()
        ]);
    }

ClientRequest.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class ClientRequest extends FormRequest
{

    /**
     * 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'
        ];
    }

    /**
     * Get the failed validation response for the request.
     *
     * @param array $errors
     * @return JsonResponse
     */
     public function response(array $errors)
     {
         dd('exit'); // Doesn't work
     }
}

【问题讨论】:

    标签: json laravel request-validation


    【解决方案1】:

    FormRequest 验证后,只需使用此 trait 即可防止重定向。 以下 trait 也带来了一些有用的公共方法,例如:

    • validatorPasses()
    • validatorFails()
    • validatorErrors()
    • respondWithErrorsJson(int $code = 422)
    • redirectWithErrors() - 恢复默认的 Laravel FomrRequest 行为

    性状

    namespace App\Http\Requests;
    
    use Illuminate\Contracts\Validation\Validator;
    use Illuminate\Http\Exceptions\HttpResponseException;
    use Illuminate\Support\MessageBag;
    use Illuminate\Validation\ValidationException;
    
    trait PreventsRedirectWhenFailedTrait
    {
        /**
         * Default self::failedValidation() Laravel behavior flag.
         *
         * @var bool
         */
        private $defaultFailedValidationRestored = false;
    
        /**
         * Check for validator success flag.
         *
         * @return bool
         */
        public function validatorPasses(): bool
        {
            return !$this->validatorFails();
        }
    
        /**
         * Check for validator fail flag.
         *
         * @return bool
         */
        public function validatorFails(): bool
        {
            return $this->getValidatorInstance()->fails();
        }
    
        /**
         * @return MessageBag
         */
        public function validatorErrors(): MessageBag
        {
            return $this->getValidatorInstance()->errors();
        }
    
        /**
         * Respond with validator errors in JSON format.
         *
         * @param  int  $code
         */
        public function respondWithErrorsJson(int $code = 422): void
        {
            if ($this->validatorFails()) {
                throw new HttpResponseException(
                    response()->json(['errors' => $this->getValidatorInstance()->errors()], $code)
                );
            }
        }
    
        /**
         * Restore and apply default self::failedValidation() method behavior.
         *
         * @throws ValidationException
         */
        public function redirectWithErrors(): void
        {
            $this->defaultFailedValidationRestored = true;
    
            $this->failedValidation($this->getValidatorInstance());
        }
    
        /**
         * Handle a failed validation attempt.
         *
         * @param  \Illuminate\Contracts\Validation\Validator  $validator
         * @return void
         *
         * @throws \Illuminate\Validation\ValidationException
         */
        protected function failedValidation(Validator $validator): void
        {
            if ($this->defaultFailedValidationRestored) {
                throw (new ValidationException($validator))
                    ->errorBag($this->errorBag)
                    ->redirectTo($this->getRedirectUrl());
            }
        }
    }
    

    使用示例:

    namespace App\Http\Requests;
    
    use Auth;
    use Illuminate\Foundation\Http\FormRequest;
    
    class AuthRequest extends FormRequest
    {
        use PreventsRedirectWhenFailedTrait;
    
        /**
         * Determine if the user is authorized to make this request.
         *
         * @return bool
         */
        public function authorize(): bool
        {
            return Auth::guest();
        }
    
        /**
         * Get the validation rules that apply to the request.
         *
         * @return array
         */
        public function rules(): array
        {
            return [
                'email' => 'required|email|exists:users',
                'password' => 'required',
                'remember_me' => 'integer',
            ];
        }
    }
    

    在您的控制器内部:

    public function authenticate(AuthRequest $request)
        {
            if ($request->validatorPasses()) {
                $data = $request->validated();
                /* your logic */
            } else {
                $errorBag = $request->validatorErrors();
            }
    
            // or
            if ($request->validatorFails()) {
                // your logic
            }
    }
    

    希望您会发现这对您有所帮助。

    【讨论】:

    • 感谢在 laravel 8 中也很有效
    【解决方案2】:

    我制作了一个中间件(仅用于 API 请求)以使 Accept 标头默认包含 application/json:

    /**
     * Ensures the default Accept header is application/json
     */
    class DefaultApiAcceptJson
    {
        public function handle(Request $request, \Closure $next)
        {
            $acceptHeader = $request->headers->get('Accept');
            if (!Str::contains($acceptHeader, 'application/json')) {
                $newAcceptHeader = 'application/json';
                if ($acceptHeader) {
                    $newAcceptHeader .= "/$acceptHeader";
                }
                $request->headers->set('Accept', $newAcceptHeader);
            }
            return $next($request);
        }
    }
    

    这样我总是得到验证错误 JSON 响应,而不是重定向到 Web 索引页面。

    【讨论】:

      【解决方案3】:

      两种方法来处理验证器错误,我的建议是第二种方法

      1. 第一种方式,当验证失败时(在控制器中)简单地返回一个错误。 示例:

          try {
              request()->validate([
                  'input1' => 'required',
                  'input2' => 'string|min:5',
              ]);
      
          } catch (\Illuminate\Validation\ValidationException $e){
              return response('The given data was invalid.', 400);
      
          }
      

      方便又干净。

      2. 第二种方法是向用户(在控制器中)显示完整错误,如下所示:

          use Illuminate\Support\Facades\Validator;
      
          $validator = Validator::make(request()->all(), [
               'id' => 'required|integer',
               'description' => 'string'
          ]);
      
          // return array of errors to client with status code 400
          if ($validator->fails())
              return response($validator->messages()->toArray(), 400);
      

      【讨论】:

        【解决方案4】:

        我刚刚创建了一个 ApiFormRequest 覆盖 FormRequest::failedValidation 这样的方法:

        <?php
        // app/Http/Requests/ApiFormRequest.php
        namespace App\Http\Requests;
        
        use Illuminate\Foundation\Http\FormRequest;
        use Illuminate\Http\Exceptions\HttpResponseException;
        use Illuminate\Contracts\Validation\Validator;
        
        class ApiFormRequest extends FormRequest
        {
        
            protected function failedValidation(Validator $validator): void
            {
                $jsonResponse = response()->json(['errors' => $validator->errors()], 422);
        
                throw new HttpResponseException($jsonResponse);
            }
        }
        

        那你就这么用吧

        <?php
        namespace App\Http\Requests;
        
        use Illuminate\Foundation\Http\FormRequest;
        
        class ClientRequest extends ApiFormRequest
        {
            // ...
        

        【讨论】:

          【解决方案5】:

          在发出请求时,我们应该发送标头信息。

          Accept: application/json
          Content-Type: application/json
          

          就是这样,现在 laravel 不会重定向并以 JSON 格式发送错误消息。

          【讨论】:

          • 是的,这就是我现在使用邮递员时所面临的问题。验证失败时没有错误消息。它只是重定向到索引页面。谢谢,伙计...
          • 您可以在收藏的预请求脚本中设置自动设置Accept 标头:pm.request.headers.upsert({ key: 'Accept', value: 'application/json' })
          【解决方案6】:

          试试这个

          打开 app/Exceptions/Handler.php 文件

          包括使用

          use Illuminate\Validation\ValidationException;
          

          然后添加方法

              /**
               * Create a response object from the given validation exception.
               *
               * @param  \Illuminate\Validation\ValidationException  $e
               * @param  \Illuminate\Http\Request  $request
               * @return \Symfony\Component\HttpFoundation\Response
               */
              protected function convertValidationExceptionToResponse(ValidationException $e, $request)
              {
                  if ($e->response) {
                      return $e->response;
                  }
          
                  return response()->json($e->validator->errors()->getMessages(), 422);
              }
          

          现在您可以获得标准的验证失败响应,例如 ajax 请求

          【讨论】:

            【解决方案7】:

            你可以这样试试

            在您的表单请求中首先包含如下用途

            use Illuminate\Contracts\Validation\Validator;
            use Illuminate\Http\Exceptions\HttpResponseException;
            

            然后

            protected function failedValidation(Validator $validator) {
                    throw new HttpResponseException(response()->json($validator->errors(), 422));
                }
            

            现在,如果您尝试验证,那么它将像这样返回

            {
            "title": [
            "The  title field is required."
            ]
            }
            

            【讨论】:

            • 正是我要找的东西!
            • 在网上看到一个经过深思熟虑的 Laravel 问题和匹配的答案真是令人耳目一新!
            猜你喜欢
            • 2018-03-28
            • 2017-12-24
            • 2017-10-20
            • 2017-11-30
            • 1970-01-01
            • 2019-05-29
            • 2019-10-07
            • 2017-11-13
            • 2017-07-19
            相关资源
            最近更新 更多