vapor-docs/docs/basics/validation.zh.md

206 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Validation API
Vapor 的 **Validation API** 可帮助你在使用 [Content](content.md) API 解码数据之前,对传入的请求进行验证。
## 介绍
Vapor 对 Swift 的类型安全的`可编码`协议进行了深度集成,这意味着与动态类型的语言相比,你无需担心数据验证。但是,出于某些原因,你可能想选择使用 **Validation API** 进行显式验证。
### 语义可读错误
如果取得的数据无效,使用 [Content](content.md) API 对其解码将产生错误。但是,这些错误消息有时可能缺乏可读性。例如,采用以下字符串支持的枚举:
```swift
enum Color: String, Codable {
case red, blue, green
}
```
如果用户尝试将字符串“purple”传递给“Color”类型的属性则将收到类似于以下内容的错误
```
Cannot initialize Color from invalid String value purple for key favoriteColor
```
尽管此错误在技术上是正确的,并且可以成功地保护端点免受无效值的影响,但它可以更好地通知用户该错误以及可用的选项。通过使用 **Validation API**,你可以生成类似以下的错误:
```
favoriteColor is not red, blue, or green
```
此外,一旦遇到第一个错误,`Codable` 将停止尝试解码。这意味着即使请求中有许多无效属性,用户也只会看到第一个错误。 **Validation API** 将在单个请求中抛出所有的验证失败信息。
### 特殊验证
`Codable` 可以很好地处理类型验证,但是有时候你还想要更多的验证方式。例如,验证字符串的内容或验证整数的大小。**Validation API** 具有此类验证器,可帮助验证电子邮件、字符集、整数范围等数据。
## 验证
为了验证请求,你需要生成一个 `Validations` 集合。最常见的做法是使现有类型继承 **Validatable**
让我们看一下如何向这个简单的 `POST/users` 请求添加验证。本指南假定你已经熟悉 [Content](content.md) API。
```swift
enum Color: String, Codable {
case red, blue, green
}
struct CreateUser: Content {
var name: String
var username: String
var age: Int
var email: String
var favoriteColor: Color?
}
app.post("users") { req -> CreateUser in
let user = try req.content.decode(CreateUser.self)
// Do something with user.
return user
}
```
### 添加验证
第一步是在你要解码的类型(在本例中为 CreateUser继承 **Validatable** 协议并实现 `validations` 静态方法,可在 `extension` 中完成。
```swift
extension CreateUser: Validatable {
static func validations(_ validations: inout Validations) {
// Validations go here.
}
}
```
验证`CreateUser`后,将调用静态方法 `validations_ :)`。你要执行的所有验证都应添加到 **Validations** 集合中。让我们添加一个简单的验证,以验证用户的电子邮件是否有效。
```swift
validations.add("email", as: String.self, is: .email)
```
第一个参数是参数值的预期键,在本例中为`email`。这应与正在验证的类型上的属性名称匹配。第二个参数`as`是预期的类型,在这种情况下为`String`。该类型通常与属性的类型匹配。最后,可以在第三个参数`is`之后添加一个或多个验证器。在这种情况下,我们添加一个验证器,以检查该值是否为电子邮件地址。
### 验证请求的 `Content`
当你的数据类型继承了 **Validatable**,就可以使用 `validate(content:)` 静态方法来验证请求的 `content`。在路由处理程序中 `req.content.decode(CreateUser.self)` 之前添加以下行:
```swift
try CreateUser.validate(content: req)
```
现在,尝试发送以下包含无效电子邮件的请求:
```http
POST /users HTTP/1.1
Content-Length: 67
Content-Type: application/json
```
你应该能看到返回以下错误:
```
email is not a valid email address
```
### 验证请求的 `Query`
当你的数据类型继承了 **Validatable**,就可以使用 `validate(query:)` 静态方法来验证请求的 `query`。在路由处理程序中添加以下行:
```swift
try CreateUser.validate(query: req)
req.query.decode(CreateUser.self)
```
现在,尝试发送一下包含错误的 email 在 query 的请求。
```http
GET /users?age=4&email=foo&favoriteColor=green&name=Foo&username=foo HTTP/1.1
```
你将会看到下面的错误:
```
email is not a valid email address
```
### 整数验证
现在让我们尝试添加一个针对整数年龄的验证:
```swift
validations.add("age", as: Int.self, is: .range(13...))
```
年龄验证要求年龄大于或等于`13`。如果你尝试发送一个和上面相同的请求,现在应该会看到一个新错误:
```
age is less than minimum of 13, email is not a valid email address
```
### 字符串验证
接下来,让我们添加对“名称”和“用户名”的验证。
```swift
validations.add("name", as: String.self, is: !.empty)
validations.add("username", as: String.self, is: .count(3...) && .alphanumeric)
```
名称验证使用 `!` 运算符将 `.empty` 验证反转。这要求该字符串不为空。
用户名验证使用`&&`组合了两个验证器。这将要求该字符串的长度至少为3个字符并且使用 && 来包含字母数字字符。
### 枚举验证
最后,让我们看一下更高级的验证,以检查提供的`favoriteColor`是否有效:
```swift
validations.add("favoriteColor", as: String.self,is: .in("red", "blue","green"),required: false)
```
由于无法从无效值中解码“颜色”,因此此验证将“字符串”用作基本类型。它使用 .in 验证器来验证该值是有效的选项:红色、蓝色或绿色。由于该值是可选的,因此将`required`设置为 false 表示如果请求数据中缺少此字段,则验证不会失败。
请注意,如果缺少此字段,则收藏夹颜色验证将通过,但如果提供 `null`,则不会通过。 如果要支持`null`,请将验证类型更改为`String?`,并使用 `.nil ||`
```swift
validations.add("favoriteColor", as: String?.self,is: .nil || .in("red", "blue", "green"),required: false)
```
## 验证器
以下是当前支持的验证器的列表,并简要说明了它们的作用:
|验证方式|描述|
|:--|:--|
|`.ascii`|仅包含ASCII字符|
|`.alphanumeric`|仅包含字母数字字符|
|`.characterSet(_:)`|仅包含提供的 `CharacterSet` 中的字符|
|`.count(_:)`|在提供范围内的集合计数|
|`.email`|包含有效的电子邮件|
|`.empty`|集合为空|
|`.in(_:)`|值在提供的“集合”中|
|`.nil`|值为`null`|
|`.range(_:)`|值在提供的范围内|
|`.url`|包含有效的URL|
验证器也可以使用运算符组合起来以构建复杂的验证:
|操作符|位置|描述|
|:--|:--|:--|
|`!`|前面|反转验证器,要求相反|
|`&&`|中间|组合两个验证器,需要同时满足|
|`||`|中间|组合两个验证器,至少满足一个|