【问题标题】:Validate or remove for extra fields in laravel验证或删除 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 个字段的表单时,我只想要服务器端请求中的那些字段,如果有人添加任何其他具有值的字段,那么我想拒绝该请求或从请求中删除该附加字段。

标签: php forms laravel


【解决方案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');
          }
          

          【讨论】:

            猜你喜欢
            • 2020-10-22
            • 2022-09-28
            • 2015-06-22
            • 2011-04-14
            • 2015-06-12
            • 2019-03-17
            • 2018-09-11
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多