验证或删除 laravel 中的额外字段
Posted
技术标签:
【中文标题】验证或删除 laravel 中的额外字段【英文标题】:Validate or remove for extra fields in laravel 【发布时间】:2016-05-21 12:59:31 【问题描述】:是否可以使用其他字段的规则来验证请求,或者从请求中删除该字段?
简单的例子,我有带有规则的 FormRequest 对象:
public function rules()
return [
'id' => 'required|integer',
'company_name' => 'required|max:255',
];
当我收到任何其他字段的发布请求时,我想在控制器中获得错误/异常,或者我只想获得 id 和 company_name 字段,没有任何其他字段。它存在于 laravel 中的任何功能,还是我必须以我的方式实现它?
【问题讨论】:
我想了解你想要什么。你的意思是当一个表单发布时,表单中有很多字段,比如 5 个字段,名字,姓氏,国家,性别,年龄,而你只想挑选两个字段而忽略其他人只说性别,国家? 应该是攻击者在表单中输入附加字段的第一道防线。当我有这 5 个字段的表单时,我只想要服务器端请求中的那些字段,如果有人添加任何其他具有值的字段,那么我想拒绝该请求或从请求中删除该附加字段。 【参考方案1】:您使用 laravel 的 post request 包含的内容不仅仅是您的表单输入,因此您需要检查 Request 以查看里面的内容。
要仅获取您想要的字段,您可以使用:
$request->only(['fieldname1', 'fieldname2', 'fieldname3']);
或
$request->except(['fieldnameYouDontWant1', 'fieldnameYouDontWant2', 'fieldnameYouDontWant3']);
排除您不想要的。
【讨论】:
是的,我知道。我通过重写 FormRequest 类中的 validate 方法来做到这一点,但我想知道是否存在更好的方法?【参考方案2】:要从 Laravel 中的请求中删除密钥,请使用:
$request->request->remove('key')
【讨论】:
【参考方案3】:从 Laravel 5.5 开始,您可以在控制器中执行此操作:
public function store(StoreCompanyRequest $request)
Company::create($request->validated());
validated() 函数仅返回您已验证的字段并删除其他所有内容。
【讨论】:
完美。谢谢【参考方案4】:不幸的是,这里的大多数答案都没有抓住原始问题的重点。也就是说,您想在 FormRequest 类中指定需要验证的字段,然后在控制器中执行:
Something::create($request->validated());
但是,您可能想要验证特定字段,但不将其传递给控制器中的 create
方法。执行$request->only()
或$request->except()
的问题是您必须重复FormRequest 类中的字段,或者在模型的$guarded
属性中重复它们。
不幸的是,我发现做你想做的事情的唯一方法如下:
Something::create(collect($request->validated())->forget('field_you_dont_want')->toArray());
2020 年 10 月编辑
感谢 Laravel 论坛上的人们,在 Laravel 7 中,Request 类是可宏的。所以更好的答案是编辑app/Providers/AppServiceProvider.php
并在boot()
方法中添加:
// Extend the validated() method
Request::macro('validatedExcept', function($except = [])
return Arr::except($this->validated(), $except);
);
你需要添加
use Illuminate\Support\Arr;
也到app/Providers/AppServiceProvider.php
的顶部。
那么你就可以使用了:
$request->validatedExcept(['field_to_ignore', 'another_field_to_ignore']);
【讨论】:
是的,这是问题的直接答案。如果我们使用$request->validated(),那么必须遵循这个方法。【参考方案5】:您可以使用 except 请求方法,该方法将为您提供除给定字段之外的字段
$request = $request->except('your_field');
如果您想从请求中删除多个字段,您可以传递字段数组
$request = $request->except(['field_1','field_2',...])
Reference: Laravel requests
【讨论】:
这不能按预期工作。$request->except
生成一个数组,因此设置 $request
变量会导致类型更改【参考方案6】:
从 Laravel 5.5 开始,您可以直接在 Request
上调用 validate
方法,例如
class YourController extends Controller
public function store(Request $request)
$cleanData = $request->validate([
'id' => 'required|integer',
'company_name' => 'required|max:255',
]);
// Now you may safely pass $cleanData right to your model
// because it ONLY contains fields that in your rules e.g
$yourModel->create($cleanData);
【讨论】:
这是一个很好的答案。正如预期的那样,我也可以在 L8 中看到这种情况,但我找不到任何有关此更改的来源。这是否记录在某处? 请记住,只有分配的变量会以这种方式运行,在本例中为 $cleanData。你原来的 $request 对象没有改变,仍然包含所有原来的未验证字段。because it ONLY contains fields that in your rules
- 我试图找到这个文件,但没有运气。对 Lavarel 文档感到沮丧。【参考方案7】:
当您开始进行嵌套数组元素验证时,事情会变得有些棘手,尤其是在涉及通配符的情况下。将其放入您的自定义 Request
类中,并将其注入您的控制器方法中。应该涵盖所有情况。
public function authorize()
//Dot notation makes it possible to parse nested values without recursion
$original = array_dot($this->all());
$filtered = [];
$rules = collect($this->rules());
$keys = $rules->keys();
$rules->each(function ($rules, $key) use ($original, $keys, &$filtered)
//Allow for array or pipe-delimited rule-sets
if (is_string($rules))
$rules = explode('|', $rules);
//In case a rule requires an element to be an array, look for nested rules
$nestedRules = $keys->filter(function ($otherKey) use ($key)
return (strpos($otherKey, "$key.") === 0);
);
//If the input must be an array, default missing nested rules to a wildcard
if (in_array('array', $rules) && $nestedRules->isEmpty())
$key .= ".*";
foreach ($original as $dotIndex => $element)
//fnmatch respects wildcard asterisks
if (fnmatch($key, $dotIndex))
//array_set respects dot-notation, building out a normal array
array_set($filtered, $dotIndex, $element);
);
//Replace all input values with the filtered set
$this->replace($filtered);
//If field changes were attempted, but non were permitted, send back a 403
return (empty($original) || !empty($this->all()));
【讨论】:
不错的解决方案!我将$rules = explode('|', $rules);
更改为 if (is_string($rules)) $rules = explode('|', $rules);
以使其也适用于基于数组的规则。
更新了 sn-p 以包含该调整。谢谢!【参考方案8】:
我有一个新的解决方案,在你的表单请求类中创建一个新函数:
public function onlyInRules()
return $this->only(array_keys($this->rules()));
然后在您的控制器中,调用 $request->onlyInRules()
而不是 $request->all()
【讨论】:
【参考方案9】:我是通过重写 FormRequest 类中的方法 validate() 来实现的。
abstract class MyFormRequest extends FormRequest
public function validate()
foreach ($this->request->all() as $key => $value)
if (!array_key_exists($key, $this->rules()))
throw new ValidationException("Field " . $key . " is not allowed.");
parent::validate();
我认为这不是最好的方法,但它确实有效。我应该升级它以一次显示所有错误字段的信息:)
【讨论】:
可以直接使用$this->has('field_name')并抛出异常,无需循环【参考方案10】:你可以通过你的控制器来做到这一点。您只能将两个或某些字段发送到数据库。使用 $request->only()
而不是 $request->all()
并传递要保存在数据库中的数据数组。您的controller
的store()
方法应如下所示:
public function store(Request $request)
ModelName::create($request->only(['id','company_name']));
return redirect('RouteName');
【讨论】:
是的,这会起作用,但我想为此使用 FormRequests,我不想第二次指定我的表单中应该包含的内容。我第一次在 FormRequest 中使用规则执行此操作时,我想重用该规则。当您在应用程序中有很多表格时,很容易忘记一些想法。【参考方案11】:$request->validated();
将只返回经过验证的数据。
在你的控制器方法中:
public function myController(ValidationRequest $request)
$data = $request->validated();
【讨论】:
我认为这不是问题的答案...请参阅上面的答案...【参考方案12】:由于没有人从“测试”的角度回答这个问题,你可以做的是重置请求查询。
request()->query = new \Symfony\Component\HttpFoundation\InputBag();
【讨论】:
【参考方案13】:Laravel 有一个“排除”验证规则: Validation: Exclude
表单请求
public function rules()
return [
'second_hand_smoke' => 'required|boolean',
'flu_vaccination' => 'required|boolean',
'provided' => 'required|boolean|exclude',
];
控制器
$request->validated();
返回
array:2[
"second_hand_smoke" => 1
"flu_vaccination" => 1
]
【讨论】:
【参考方案14】:这是我认为最好的方法
$this->offsetUnset('field_1');
在表单请求验证中,您可以像这样使用
protected function prepareForValidation()
$this->offsetUnset('field_1');
【讨论】:
以上是关于验证或删除 laravel 中的额外字段的主要内容,如果未能解决你的问题,请参考以下文章
Laravel 5.1 使用默认身份验证控制器和中间件尝试使用额外参数进行身份验证
Laravel 验证规则 - 要求字段显式为空,具体取决于另一个字段的值 (type_id)
单击按钮时附加新文本字段并通过单击 Laravel 4 中的按钮删除