Skip to content

第2.6章:数据验证

学习时间: 50 分钟


1. 验证:宇宙级防护盾

验证是指检查传入数据是否符合规则。没有它:

  • 🚀 不正确的数据可能会“破坏”你的数据库
  • 🌌 恶意攻击者可能会注入恶意代码
  • 🪐 用户会收到难以理解的错误

💡 宇宙类比: 验证 = 空间站防御系统:

  • 在对接前检查“货物”(数据)
  • 拒绝危险物体
  • 过滤太空碎片

2. 在 Laravel API 中何处进行验证

主要方法:

  1. 在控制器中 (快捷,但会使代码臃肿)
  2. 表单请求 (Form Request) (推荐,架构清晰)

3. 在控制器中进行验证

使用 Request 对象的 validate() 方法:

<?php
public function store(Request $request)
{
    $validated = $request->validate([
        'name' => 'required|string|max:255|unique:planets',
        'description' => 'required|string',
        'size_km' => 'required|integer|min:100|max:500000',
        'solar_system' => 'required|string|max:100',
        'image_url' => 'nullable|url|max:2048',
        'is_habitable' => 'boolean'
    ]);

    // ... 创建星球
}

常用验证规则:

规则 描述 示例
required 必填字段 'name' => 'required'
string 字符串值 'description' => 'string'
integer 整数 'size_km' => 'integer'
min:value 最小值/最小长度 'size_km' => 'min:100'
max:value 最大值/最大长度 'name' => 'max:255'
unique:table,column 表中唯一性 'name' => 'unique:planets'
url 有效 URL 'image_url' => 'url'
boolean true/false/1/0 'is_habitable' => 'boolean'

4. 自定义错误消息

修改默认的错误文本:

<?php
$validated = $request->validate(
    [
        'name' => 'required|unique:planets',
        'size_km' => 'min:1000'
    ],
    [
        'name.required' => '星球名称是必填的!',
        'name.unique' => '目录中已存在同名星球',
        'size_km.min' => '星球直径不能小于 1000 公里'
    ]
);

错误响应示例 (自动返回 422 Unprocessable Entity):

{
    "message": "The given data was invalid.",
    "errors": {
        "name": ["目录中已存在同名星球"],
        "size_km": ["星球直径不能小于 1000 公里"]
    }
}


5. 创建表单请求 (Form Request)

对于复杂的验证,我们创建一个单独的类:

步骤 1:生成

php artisan make:request StorePlanetRequest

步骤 2:编辑 app/Http/Requests/StorePlanetRequest.php

<?php
public function authorize()
{
    return true; // 对于 API 通常为 true
}

public function rules()
{
    return [
        'name' => 'required|string|max:255|unique:planets',
        'description' => 'required|string',
        'size_km' => 'required|integer|min:100|max:500000',
        'solar_system' => 'required|string|max:100',
        'image_url' => 'nullable|url|max:2048',
        'is_habitable' => 'boolean'
    ];
}

public function messages()
{
    return [
        'name.unique' => '同名星球已存在!',
        'size_km.min' => '直径不能小于 100 公里'
    ];
}

步骤 3:在控制器中使用

<?php
use App\Http\Requests\StorePlanetRequest;

public function store(StorePlanetRequest $request)
{
    // 数据已验证!
    $validated = $request->validated();
    $planet = Planet::create($validated);
    return response()->json($planet, 201);
}


6. 自定义验证规则

我们将创建一个规则来检查星球名称的“合理性”。Laravel 的标准规则无法检查名称是否被“禁止”,所以我们将编写自己的逻辑。

步骤 1:生成规则

Laravel 提供了一个 Artisan 命令来创建规则类的“骨架”。在终端中执行它:

php artisan make:rule ValidPlanetName

步骤 2:编辑 app/Rules/ValidPlanetName.php

打开创建的文件。其结构简单明了。我们的任务是在 validate 方法内部实现逻辑。

<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class ValidPlanetName implements ValidationRule
{
    /**
     * 运行验证规则。
     *
     * @param  \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString  $fail
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        // 我们的名称“黑名单”
        $forbidden = ['Земля 2.0', 'Нибиру', 'Планета X'];

        // 检查输入值是否在我们的列表中,
        // 忽略大小写。
        if (in_array(strtolower($value), array_map('strtolower', $forbidden))) {
            // 如果验证未通过,调用 $fail 函数
            // 并附上用户将看到的错误文本。
            $fail('此星球名称禁止使用!');
        }
    }
}

步骤 3:在表单请求 (Form Request) 中使用

现在我们的自定义规则已准备就绪。我们可以在任何表单请求中引用它,只需创建一个我们的类的新实例即可。

打开 app/Http/Requests/StorePlanetRequest.php 并将 new ValidPlanetName 添加到 name 字段的规则数组中。

<?php
// app/Http/Requests/StorePlanetRequest.php

namespace App\Http\Requests;

use App\Rules\ValidPlanetName; // <-- 别忘了导入类
use Illuminate\Foundation\Http\FormRequest;

class StorePlanetRequest extends FormRequest
{
    // ... (authorize 方法)

    public function rules(): array
    {
        return [
            'name' => [
                'sometimes',
                'string',
                'max:255',
                'unique:planets',
                new ValidPlanetName, // <-- 这就是我们的自定义规则
            ],
            'description' => 'sometimes|string',
            'size_km' => 'sometimes|integer|min:100|max:500000',
            'solar_system' => 'sometimes|string|max:100',
            'image_url' => 'nullable|url|max:2048',
            'is_habitable' => 'sometimes|boolean'
        ];
    }

    // ... (messages 方法)
}
完成。现在,当创建星球时,Laravel 将依次对 name 字段应用所有规则,并在遇到 new ValidPlanetName 时执行我们的自定义逻辑。


7. 用于更新 (Update) 的验证

更新数据时的特点:

更新记录时,验证规则通常有所不同。主要的特点是唯一性检查,它应该忽略当前正在更新的记录。

步骤 1:为更新创建一个单独的表单请求 (Form Request)

php artisan make:request UpdatePlanetRequest
步骤 2:编辑 app/Http/Requests/UpdatePlanetRequest.php
<?php
use Illuminate\Validation\Rule;

public function authorize(): bool
{
    return true;
}
public function rules(): array
{
    $planet = $this->route('planet'); // 从路由中获取模型

     return [
         'name' => [
             'sometimes', // 仅当字段存在于请求中时才进行验证
             'required',
             'string',
             'max:255',
             Rule::unique('planets')->ignore($planetId),
         ],
         'description'  => 'sometimes|required|string',
         'size_km'      => 'sometimes|required|integer|min:100|max:500000',
         // ... 其他带有 'sometimes' 的字段
     ];
 }
步骤 3:在控制器中使用
<?php
 use App\Http\Requests\UpdatePlanetRequest;

 public function update(UpdatePlanetRequest $request, Planet $planet)
 {
     $validated = $request->validated();
     $planet->update($validated);
     return response()->json($planet);
 }


8. 在 Postman 中测试验证

场景 1:名称唯一性错误

POST /api/planets
{
    "name": "Марс",
    "description": "Красная планета, цель будующих колонизаций",
    "size_km": 6779,
    "solar_system": "Solar System",
    "is_habitable": false
}
预期响应:
{
    "message": "The given data was invalid.",
    "errors": {
        "name": ["同名星球已存在!"]
    }
}

场景 2:直径不正确

{
    "name": "Планета-Горошина",
    "size_km": 50 // < min:100
}
预期响应:
"errors": {
    "size_km": ["直径不能小于 100 公里"]
}


巩固知识的测验

1. 验证错误时的 HTTP 状态码是:

2. 用于检查唯一性的规则是:

3. 复杂的验证规则最好放在哪里?

4. Form Request 中的 `authorize()` 方法应返回:

5. sometimes 规则意味着:

🚀 章节总结:

您已为您的宇宙 API 建立了强大的保护系统:

  • 🛡️ 基本和自定义验证规则
  • 📝 可读的错误消息
  • 🧩 Form Request 用于复杂场景
  • ⚙️ 更新数据的唯一规则

您的宇宙现已受到保护! 接下来,我们将学习如何处理“宇宙事故”——服务器错误。

📌 检查:

  1. 创建用于更新行星的 Form Request
  2. 添加自定义名称验证规则
  3. 通过 Postman 测试错误

⚠️ 如果验证失败:

  • 检查控制器中 Form Request 的引入
  • 确保 authorize() 返回 true
  • 对于更新时的唯一性,请使用 Rule::unique