验证或删除 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() 并传递要保存在数据库中的数据数组。您的controllerstore() 方法应如下所示:

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 中的按钮删除

Laravel中的信息验证 和 语言包

身份验证中的 Laravel Angular CORS 问题

数据库中的计算字段 - Laravel