Php Laravel验证:仅允许已知属性/属性,否则验证失败

Php Laravel验证:仅允许已知属性/属性,否则验证失败,php,laravel,validation,Php,Laravel,Validation,我们正在构建一个需要精确性的api端点。我们希望对POST/PUT到服务器的参数实施严格的验证 如果api用户发送了一个不受支持的key=value对(例如,我们允许参数[first\u name,last\u name],并且用户包含一个不受支持的参数[country]),我们希望验证失败 已尝试构建名为允许的\u属性(用作允许的\u属性:attr1、attr2、)的自定义验证器,但要使其在$validationRules数组中可用,必须将其应用于嵌套/子属性列表的父级(…因为我们的自定义验证

我们正在构建一个需要精确性的api端点。我们希望对POST/PUT到服务器的参数实施严格的验证

如果api用户发送了一个不受支持的
key=value
对(例如,我们允许参数[first\u name,last\u name],并且用户包含一个不受支持的参数[country]),我们希望验证失败

已尝试构建名为
允许的\u属性
(用作
允许的\u属性:attr1、attr2、
)的自定义验证器,但要使其在
$validationRules
数组中可用,必须将其应用于嵌套/子属性列表的父级(…因为我们的自定义验证器无法访问正在验证的属性)

这就给其他验证器带来了问题,我们必须预测父/子结构及其周围的代码,包括额外的验证后错误键和错误消息字符串的清理

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属性列表(在自定义验证器中)?

它应该适用于此自定义验证器的简单键/值对:

Validator::extendImplicit('allowed_attributes',function($attribute,$value,$parameters,$Validator){
//如果要验证请求的属性为顶级
if(strpos($attribute,'.')==false){
返回数组($attribute,$parameters);
}
//如果验证中的属性是数组
if(是_数组($value)){
返回空(array_diff_key($value,array_flip($parameters));
}
//如果验证中的属性是对象
foreach($parameters作为$parameter){
if(substr_compare($attribute,$parameter,-strlen($parameter))==0){
返回true;
}
}
返回false;
});
验证器逻辑非常简单:

  • 如果
    $attribute
    不包含
    ,我们将处理一个顶级参数,我们只需检查它是否存在于传递给规则的
    允许的属性列表中
  • 如果
    $attribute
    的值是一个数组,我们将输入键与
    allowed_attributes
    列表区分,并检查是否有任何属性键离开。如果是,我们的请求有一个我们不期望的额外键,因此我们返回
    false
  • 否则,
    $attribute
    的值就是一个对象,我们必须检查我们期望的每个参数(同样,
    允许的属性列表)是否是当前属性的最后一段(因为laravel在
    $attribute
    中为我们提供了完整的点标记属性)
这里的关键是将其应用于验证规则,如下所示(请注意第一条验证规则):

$validationRules=[
'parent.*'=>'允许的\u属性:名字、姓氏',
'parent.first_name'=>'必填|字符串|最大值:40',
'parent.last_name'=>'必填|字符串|最大值:40'
];
parent.*
规则将对“parent”对象的每个键应用自定义验证器

回答你的问题 只是不要将您的请求包装在对象中,而是使用与上面相同的概念,并使用
*
应用
允许的属性
规则:

$validationRules=[
“*”=>“允许的\u属性:名字、姓氏”,
'first_name'=>'必填|字符串|最大值:40',
“姓氏”=>“必填”|字符串|最大值:40”
];
这将把规则应用于所有当前的顶级输入请求字段


注意:请记住,在规则数组中放置规则时,laravel验证受规则顺序的影响。 例如,移动底部的
父项。*
规则将触发
父项.first_name
父项.last_name
上的该规则;相反,将其保留为第一条规则不会触发对
first_name
last_name
的验证

这意味着您最终可以从
allowed\u attributes
规则的参数列表中删除具有进一步验证逻辑的属性

例如,如果希望仅要求名和姓,并禁止父对象中的任何其他字段,则可以使用以下规则:

$validationRules=[
//除名字和姓氏外,所有请求字段都会触发此操作
'父项。*'=>'允许的\u属性',
'parent.first_name'=>'必填|字符串|最大值:40',
'parent.last_name'=>'必填|字符串|最大值:40'
];
但是,以下操作将无法按预期工作:

$validationRules=[
'parent.first_name'=>'必填|字符串|最大值:40',
'parent.last_name'=>'必填|字符串|最大值:40',
//相反,这将在所有字段上触发,也会在first_name和last_name上触发
//如果将此规则放在最后,则必须指定允许的字段。
'父项。*'=>'允许的\u属性',
];

阵列小问题 据我所知,根据Laravel的验证逻辑,如果您要验证一个对象数组,那么这个自定义验证器可以工作,但是您将在数组项上得到通用的错误消息,而不是在不允许的数组项的键上

例如,您在请求中允许一个products字段,每个字段都有一个id:

$validationRules=[
'products.*'=>'允许的\u属性:id',
];
如果您验证这样的请求:

{
    "products": [{
        "id": 3
    }, {
        "id": 17,
        "price": 3.49
    }]
}

产品2上会出现错误,但您无法判断是哪个字段导致了问题!

此自定义验证器适用于简单的键/值对:

Validator::extendImplicit('allowed_attributes',function($attribute,$value,$parameters,$Validator){
//如果属性
{
    "products": [{
        "id": 3
    }, {
        "id": 17,
        "price": 3.49
    }]
}