Laravel 请求没有 return 修改后的请求验证失败
Laravel Request does not return the modified request on validation fail
我最近更新了我的控制器以在保存之前使用请求来验证数据,最初我在控制器路由中使用 $request->validate()
,但我现在正处于一个我真的需要将其分离出来的阶段一个请求。
问题
在进行验证之前,我需要更改请求中的一些参数,我发现这可以使用 prepareForValidation()
方法来完成,并且在验证请求中的值期间效果很好已被更改。如果验证失败,我的问题就来了。我需要能够 return 我已经更改回视图的请求,目前,在重定向之后它似乎正在使用我之前的请求 运行 prepareForValidation()
. (即 return 的标题为 'ABCDEFG' 而不是 'Changed The Title')。
在阅读了其他 SO posts 和 Laravel 论坛 posts 之后,看起来我需要覆盖 FormRequest::failedValidation()
方法(我已经完成,请参见下面的代码),但是我仍在努力寻找一种方法来传回我更改后的请求。我已尝试编辑 failedValidation()
方法,我在下方提供了详细信息。
期望与现实
预期
- 用户输入 'ABCDEFG' 作为标题并按保存。
- 请求中的标题(使用
prepareForValidation()
)更改为 'Changed The Title'。
- 验证失败,用户被重定向回创建页面。
- 标题字段的内容现在是“更改了标题”。
现实
- 用户输入 'ABCDEFG' 作为标题并按保存。
- 请求中的标题(使用
prepareForValidation()
)更改为 'Changed The Title'。
- 验证失败,用户被重定向回创建页面。
- 标题字段的内容显示“ABCDEFG”。
我试过的
正在将请求传递给 ValidationException
class。
深入研究代码后,看起来 ValidationException
允许将响应作为参数传递。
protected function failedValidation(Validator $validator)
{
throw (new ValidationException($validator, $this))
->errorBag($this->errorBag)
->redirectTo($this->getRedirectUrl());
}
然而,这会导致 Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::addCookieToResponse
中的错误 Call to undefined method Symfony\Component\HttpFoundation\HeaderBag::setCookie()
。
将请求闪烁到 session
我的下一次尝试是将我的请求闪现到 session,这似乎不起作用,而不是我修改后的请求在 session,它看起来是请求在我 运行 prepareForValidation()
.
之前
protected function failedValidation(Validator $validator)
{
$this->flash();
throw (new ValidationException($validator))
->errorBag($this->errorBag)
->redirectTo($this->getRedirectUrl());
}
返回响应而不是异常
我最后的尝试是 return 使用 withInput()
而不是异常的响应。
protected function failedValidation(Validator $validator)
{
return redirect($this->getRedirectUrl())
->withErrors($validator)
->withInput();
}
但是看起来代码继续进入 BlogPostController::store()
方法而不是重定向回视图。
在这一点上我没有想法,如果验证失败,我似乎无法将更改后的请求返回到视图!
其他注意事项
- 我几乎是一个 Laravel 新手,我有使用松散地基于 Laravel 的自定义框架的经验,但这是我的第一个 CMS 项目。
- 我完全理解我可能走错了路,也许有更好的方法来更改请求并在验证失败时将其传回?
- 我这样做的目的是什么?最主要的是活动复选框。默认情况下是勾选的(见下面的blade),如果用户取消勾选并保存,HTTP请求中不会传递
active
,因此active
不存在Laravel 请求 object 并且当用户 return 返回时,激活的复选框在不应该的时候再次被选中。
- 那你为什么在你的例子中使用了
title
?我在我的 post 中使用 title
因为我认为它更容易看到我想要实现的目标。
非常感谢您的帮助,因为我目前已经花了好几个小时来解决这个问题。
相关代码
BlogPostController.php
<?php
namespace App\Http\Controllers;
use App\BlogPost;
use Illuminate\Http\Request;
use App\Http\Requests\StoreBlogPost;
class BlogPostController extends Controller
{
/**
* Run the auth middleware to make sure the user is authorised.
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('admin.blog-posts.create');
}
/**
* Store a newly created resource in storage.
*
* @param StoreBlogPost $request
* @return \Illuminate\Http\Response
*/
public function store(StoreBlogPost $request)
{
$blogPost = BlogPost::create($request->all());
// Deal with the listing image upload if we have one.
foreach ($request->input('listing_image', []) as $file) {
$blogPost->addMedia(storage_path(getenv('DROPZONE_TEMP_DIRECTORY') . $file))->toMediaCollection('listing_image');
}
// Deal with the main image upload if we have one.
foreach ($request->input('main_image', []) as $file) {
$blogPost->addMedia(storage_path(getenv('DROPZONE_TEMP_DIRECTORY') . $file))->toMediaCollection('main_image');
}
return redirect()->route('blog-posts.edit', $blogPost->id)
->with('success', 'The blog post was successfully created.');
}
}
// Removed unrelated controller methods.
StoreBlogPost.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Validation\ValidationException;
class StoreBlogPost 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',
'url' => 'required',
'description' => 'required',
'content' => 'required',
];
}
/**
* Get the error messages for the defined validation rules.
*
* @return array
*/
public function messages()
{
return [
'title.required' => 'The Title is required',
'url.required' => 'The URL is required',
'description.required' => 'The Description is required',
'content.required' => 'The Content is required',
];
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'active' => $this->active ?? 0,
'title' => 'Changed The Title',
]);
}
/**
* @see FormRequest
*/
protected function failedValidation(Validator $validator)
{
throw (new ValidationException($validator))
->errorBag($this->errorBag)
->redirectTo($this->getRedirectUrl());
}
}
create.blade.php
@extends('admin.layouts.app')
@section('content')
<div class="edit">
<form action="{{ route('blog-posts.store') }}" method="POST" enctype="multipart/form-data">
@method('POST')
@csrf
<div class="container-fluid">
<div class="row menu-bar">
<div class="col">
<h1>Create a new Blog Post</h1>
</div>
<div class="col text-right">
<div class="btn-group" role="group" aria-label="Basic example">
<a href="{{ route('blog-posts.index') }}" class="btn btn-return">
<i class="fas fa-fw fa-chevron-left"></i>
Back
</a>
<button type="submit" class="btn btn-save">
<i class="fas fa-fw fa-save"></i>
Save
</button>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="form-group row">
<label for="content" class="col-12 col-xl-2 text-xl-right col-form-label">Active</label>
<div class="col-12 col-xl-10">
<div class="custom-control custom-switch active-switch">
<input type="checkbox" name="active" value="1" id="active" class="custom-control-input" {{ old('active', '1') ? 'checked' : '' }}>
<label class="custom-control-label" for="active"></label>
</div>
</div>
</div>
<div class="form-group row">
<label for="title" class="col-12 col-xl-2 text-xl-right col-form-label required">Title</label>
<div class="col-12 col-xl-10">
<input type="text" name="title" id="title" class="form-control" value="{{ old('title', '') }}">
</div>
</div>
</div>
</form>
</div>
@endsection
我遇到了同样的问题,这是我在研究 laravel 代码后找到的解决方案。
似乎 Laravel 为 FormRequest
创建了一个不同的对象,所以你可以这样做。
protected function failedValidation(Validator $validator)
{
// Merge the modified inputs to the global request.
request()->merge($this->input());
parent::failedValidation($validator);
}
我最近更新了我的控制器以在保存之前使用请求来验证数据,最初我在控制器路由中使用 $request->validate()
,但我现在正处于一个我真的需要将其分离出来的阶段一个请求。
问题
在进行验证之前,我需要更改请求中的一些参数,我发现这可以使用 prepareForValidation()
方法来完成,并且在验证请求中的值期间效果很好已被更改。如果验证失败,我的问题就来了。我需要能够 return 我已经更改回视图的请求,目前,在重定向之后它似乎正在使用我之前的请求 运行 prepareForValidation()
. (即 return 的标题为 'ABCDEFG' 而不是 'Changed The Title')。
在阅读了其他 SO posts 和 Laravel 论坛 posts 之后,看起来我需要覆盖 FormRequest::failedValidation()
方法(我已经完成,请参见下面的代码),但是我仍在努力寻找一种方法来传回我更改后的请求。我已尝试编辑 failedValidation()
方法,我在下方提供了详细信息。
期望与现实
预期
- 用户输入 'ABCDEFG' 作为标题并按保存。
- 请求中的标题(使用
prepareForValidation()
)更改为 'Changed The Title'。 - 验证失败,用户被重定向回创建页面。
- 标题字段的内容现在是“更改了标题”。
现实
- 用户输入 'ABCDEFG' 作为标题并按保存。
- 请求中的标题(使用
prepareForValidation()
)更改为 'Changed The Title'。 - 验证失败,用户被重定向回创建页面。
- 标题字段的内容显示“ABCDEFG”。
我试过的
正在将请求传递给 ValidationException
class。
深入研究代码后,看起来 ValidationException
允许将响应作为参数传递。
protected function failedValidation(Validator $validator)
{
throw (new ValidationException($validator, $this))
->errorBag($this->errorBag)
->redirectTo($this->getRedirectUrl());
}
然而,这会导致 Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::addCookieToResponse
中的错误 Call to undefined method Symfony\Component\HttpFoundation\HeaderBag::setCookie()
。
将请求闪烁到 session
我的下一次尝试是将我的请求闪现到 session,这似乎不起作用,而不是我修改后的请求在 session,它看起来是请求在我 运行 prepareForValidation()
.
protected function failedValidation(Validator $validator)
{
$this->flash();
throw (new ValidationException($validator))
->errorBag($this->errorBag)
->redirectTo($this->getRedirectUrl());
}
返回响应而不是异常
我最后的尝试是 return 使用 withInput()
而不是异常的响应。
protected function failedValidation(Validator $validator)
{
return redirect($this->getRedirectUrl())
->withErrors($validator)
->withInput();
}
但是看起来代码继续进入 BlogPostController::store()
方法而不是重定向回视图。
在这一点上我没有想法,如果验证失败,我似乎无法将更改后的请求返回到视图!
其他注意事项
- 我几乎是一个 Laravel 新手,我有使用松散地基于 Laravel 的自定义框架的经验,但这是我的第一个 CMS 项目。
- 我完全理解我可能走错了路,也许有更好的方法来更改请求并在验证失败时将其传回?
- 我这样做的目的是什么?最主要的是活动复选框。默认情况下是勾选的(见下面的blade),如果用户取消勾选并保存,HTTP请求中不会传递
active
,因此active
不存在Laravel 请求 object 并且当用户 return 返回时,激活的复选框在不应该的时候再次被选中。 - 那你为什么在你的例子中使用了
title
?我在我的 post 中使用title
因为我认为它更容易看到我想要实现的目标。
非常感谢您的帮助,因为我目前已经花了好几个小时来解决这个问题。
相关代码
BlogPostController.php
<?php
namespace App\Http\Controllers;
use App\BlogPost;
use Illuminate\Http\Request;
use App\Http\Requests\StoreBlogPost;
class BlogPostController extends Controller
{
/**
* Run the auth middleware to make sure the user is authorised.
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('admin.blog-posts.create');
}
/**
* Store a newly created resource in storage.
*
* @param StoreBlogPost $request
* @return \Illuminate\Http\Response
*/
public function store(StoreBlogPost $request)
{
$blogPost = BlogPost::create($request->all());
// Deal with the listing image upload if we have one.
foreach ($request->input('listing_image', []) as $file) {
$blogPost->addMedia(storage_path(getenv('DROPZONE_TEMP_DIRECTORY') . $file))->toMediaCollection('listing_image');
}
// Deal with the main image upload if we have one.
foreach ($request->input('main_image', []) as $file) {
$blogPost->addMedia(storage_path(getenv('DROPZONE_TEMP_DIRECTORY') . $file))->toMediaCollection('main_image');
}
return redirect()->route('blog-posts.edit', $blogPost->id)
->with('success', 'The blog post was successfully created.');
}
}
// Removed unrelated controller methods.
StoreBlogPost.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Validation\ValidationException;
class StoreBlogPost 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',
'url' => 'required',
'description' => 'required',
'content' => 'required',
];
}
/**
* Get the error messages for the defined validation rules.
*
* @return array
*/
public function messages()
{
return [
'title.required' => 'The Title is required',
'url.required' => 'The URL is required',
'description.required' => 'The Description is required',
'content.required' => 'The Content is required',
];
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'active' => $this->active ?? 0,
'title' => 'Changed The Title',
]);
}
/**
* @see FormRequest
*/
protected function failedValidation(Validator $validator)
{
throw (new ValidationException($validator))
->errorBag($this->errorBag)
->redirectTo($this->getRedirectUrl());
}
}
create.blade.php
@extends('admin.layouts.app')
@section('content')
<div class="edit">
<form action="{{ route('blog-posts.store') }}" method="POST" enctype="multipart/form-data">
@method('POST')
@csrf
<div class="container-fluid">
<div class="row menu-bar">
<div class="col">
<h1>Create a new Blog Post</h1>
</div>
<div class="col text-right">
<div class="btn-group" role="group" aria-label="Basic example">
<a href="{{ route('blog-posts.index') }}" class="btn btn-return">
<i class="fas fa-fw fa-chevron-left"></i>
Back
</a>
<button type="submit" class="btn btn-save">
<i class="fas fa-fw fa-save"></i>
Save
</button>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="form-group row">
<label for="content" class="col-12 col-xl-2 text-xl-right col-form-label">Active</label>
<div class="col-12 col-xl-10">
<div class="custom-control custom-switch active-switch">
<input type="checkbox" name="active" value="1" id="active" class="custom-control-input" {{ old('active', '1') ? 'checked' : '' }}>
<label class="custom-control-label" for="active"></label>
</div>
</div>
</div>
<div class="form-group row">
<label for="title" class="col-12 col-xl-2 text-xl-right col-form-label required">Title</label>
<div class="col-12 col-xl-10">
<input type="text" name="title" id="title" class="form-control" value="{{ old('title', '') }}">
</div>
</div>
</div>
</form>
</div>
@endsection
我遇到了同样的问题,这是我在研究 laravel 代码后找到的解决方案。
似乎 Laravel 为 FormRequest
创建了一个不同的对象,所以你可以这样做。
protected function failedValidation(Validator $validator)
{
// Merge the modified inputs to the global request.
request()->merge($this->input());
parent::failedValidation($validator);
}