我们正在构建一个需要精确度的API端点。我们希望对发送到服务器的POST/PUT参数实施严格的验证。
如果API用户发送一个key=value
对不支持(例如我们允许参数[first_name,last_name],用户包含不支持的参数[country]),我们希望验证失败。
我尝试过构建一个名为allowed_attributes
(用作allowed_attributes:attr1,attr2,...
)的自定义验证器,但要使其在$validationRules
数组中可用,必须将其应用于嵌套/子属性列表的父属性(...因为否则我们的自定义验证器无法访问正在验证的属性)。
Validator::extend('allowed_attributes', 'App\Validators\AllowedAttributesValidator@validate');
这就给其他验证器带来了问题,我们不得不预测这种父/子结构和围绕它的代码,包括对错误键和错误消息字符串的额外验证后清理。
tl;dr:非常脏,不是干净的实现。
$validationRules = [
'parent' => 'allowed_attributes:first_name,last_name',
'parent.first_name' => 'required|string|max:40',
'parent.last_name' => 'required|string|max:40'
];
$isValid = Validator::make(['parent' => $request], $validationRules);
var_dump("Validation results: " . ($isValid ? "passed" : "failed"));
关于如何在laravel中更干净地完成这一点,而不需要使用父/子关系来访问所有$request属性列表(在自定义验证器中),有什么想法/建议吗?
3条答案
按热度按时间7gcisfzg1#
我更喜欢发布一个新的答案,因为方法与前一个不同,而且更干净。因此,我宁愿将这两种方法分开,而不是在同一个答案中混合在一起。
更好的问题处理
自从我上次回答后,在深入研究了Validation的命名空间的源代码后,我发现最简单的方法是扩展Validator类来完成
passes()
函数,以检查您需要的内容。这种实现的好处是还可以正确地处理单个数组/对象字段的特定错误消息,而无需任何effor,并且应该与通常的错误消息转换完全兼容。
创建自定义验证器类
首先在app文件夹中创建一个Validator类(我把它放在
app/Validation/Validator.php
下),然后像这样实现passs方法:这实质上是扩展了默认的Validator类,以便在passs方法上添加额外的检查。检查通过转换为点表示法(以支持数组/对象验证)的输入属性和至少分配了一个规则的属性之间的键计算数组差异。
替换容器中默认的Validator
那么您错过的最后一步就是在服务提供商的** Boot 方法中绑定新的Validator类。要做到这一点,您只需覆盖绑定到IoC容器中的
Illuminate\Validation\Factory
类的解析器**作为'validator'
:在控制器中的实际使用
您不必执行任何特定操作即可使用此功能。只需像往常一样调用
validate
方法:自定义错误消息
要自定义错误消息,您只需在lang文件中添加一个转换键,其键等于
forbidden_attribute
(您可以在addFailure
方法调用的自定义Validator类中自定义错误键名称)。示例:
resources/lang/en/validation.php
**注意:**此实现仅在Laravel 5.3中测试过。
x9ybnkn62#
它应该适用于简单的键/值对与此自定义验证器:
验证器的逻辑非常简单:
$attribute
不包含.
,我们处理的是一个顶级参数,我们只需要检查它是否存在于我们传递给规则的allowed_attributes
列表中。$attribute
的值是一个数组,我们将输入键与allowed_attributes
列表进行比较,并检查是否有任何属性键离开。如果是这样,我们的请求有一个我们不期望的额外密钥,所以我们返回false
。$attribute
的值是一个对象,我们必须检查我们期望的每个参数(再次,allowed_attributes
列表)是否是当前属性的最后一段(正如laravel在$attribute
中给我们的全点标记属性)。这里的关键是将其应用于验证规则应该是这样的(注意第一个验证规则):
parent.*
规则将自定义验证器应用于“父”对象的每个键。回答您的问题
只是不要将请求 Package 在对象中,而是使用与上面相同的概念,并使用
*
应用allowed_attributes
规则:这将把规则应用于所有当前顶级输入请求字段。
parent.*
规则将触发parent.first_name
和parent.last_name
上的规则;相反,将其保留为第一规则将不会触发first_name
和last_name
的验证。这意味着您最终可以从
allowed_attributes
规则的参数列表中删除具有进一步验证逻辑的属性。例如,如果您希望在
parent
对象中只要求 * first_name * 和 * last_name *,并禁止任何其他字段,则可以使用以下规则:但是,以下 * WON'T * 工作如预期:
阵列小问题
据我所知,根据Laravel的验证逻辑,如果你要验证一个对象数组,这个自定义验证器可以工作,但是你得到的错误消息将是数组项上的通用错误消息,而不是不允许的数组项的键。
例如,您允许在请求中包含一个products字段,每个字段都有一个id:
如果你像这样验证一个请求:
您将在产品2上得到一个错误,但您将无法分辨哪个字段导致了问题!
uhry853o3#
看起来Laravel 8.50增加了一个方法,它不会抛出异常,而是简单地抛出任何没有被验证器指定的条目。
参见https://github.com/laravel/framework/pull/37943