跳转至

前面我们建立起一个可以创建缩略语、类别的web应用,当我们使用表单填写数据时,要保证填写数据符合一定的规则,如果不符合规则,就显示录入错误提示,这个过程叫作数据有效性验证。

首先我们来创建用户注册界面。

register.leaf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#set("content") {
    <h1>#(title)</h1>
    <form method="post">
        <div class="form-group">
            <label for="name">Name</label>
            <input type="text" name="name" class="form-control"
            id="name"/>
        </div>
        <div class="form-group">
            <label for="username">Username</label>
            <input type="text" name="username" class="form-control"
            id="username"/>
        </div>
        <div class="form-group">
            <label for="password">Password</label>
            <input type="password" name="password" class="form-control"
            id="password"/>
        </div>
        <div class="form-group">
            <label for="confirmPassword">Confirm Password</label>
            <input type="password" name="confirmPassword"
            class="form-control" id="confirmPassword"/>
        </div>
        <button type="submit" class="btn btn-primary">
            Register
        </button>
    </form>
}
#embed("base")

WebsiteController.swift

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
struct RegisterContext: Encodable {
    let title = "Register"
}

struct RegisterData: Content {
    let name: String
    let username: String
    let password: String
    let confirmPassword: String
}
func boot(router: Router) throws {
        ...
        authSessionRoutes.get("register", use: registerHandler)
        authSessionRoutes.post(RegisterData.self, at: "register", use: registerPostHandler)
        ...
    }

    ...
    func registerHandler(_ req: Request) throws -> Future<View> {
        let context = RegisterContext()
        return try req.view().render("register", context)
    }
    func registerPostHandler(_ req: Request, data: RegisterData) throws -> Future<Response> {
        let password = try BCrypt.hash(data.password)
        let user = User(name: data.name, username: data.username, password: password)

        return user.save(on: req).map(to: Response.self) { user in
            try req.authenticateSession(user)
            return req.redirect(to: "/")
        }
    }    
    ...

base.leaf

1
2
3
4
5
6
7
8
...
        #if(!userLoggedIn) {
            <li class="nav-item #if(title == "Register"){active}">
                <a href="/register" class="nav-link">Register</a>
            </li>
        }
    </ul>
...

register-user

注册用户时的数据用效性验证

基本数据有效性验证

WebsiteController.swift

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
extension RegisterData: Validatable, Reflectable {
    static func validations() throws -> Validations<RegisterData> {
        var validations = Validations(RegisterData.self)
        try validations.add(\.name, .ascii)
        try validations.add(\.username, .alphanumeric && .count(3...))
        try validations.add(\.password, .count(8...))
        return validations
    }
}
...
    func registerPostHandler(_ req: Request, data: RegisterData) throws -> Future<Response> {
        do {
            try data.validate()
        } catch {
            return req.future(req.redirect(to: "register"))
        }
        ...
    }

自定义数据有效性验证

前面添加的数据有效性验证都是针对单个字段的,但是两次输入密码的一致性验证vapor并没有提供内建的方法,我们需要使用自定义有效性验证

WebsiteController.swift

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
extension RegisterData: Validatable, Reflectable {
    static func validations() throws -> Validations<RegisterData> {
        ...
        validations.add("passwords match", { (model) in
            guard model.password == model.confirmPassword else {
                throw BasicValidationError("passwords don't match")
            }
        })
        return validations
    }
}

注册页面显示错误信息

前面完成了数据有性效验证,但发生错误时没有途径通知用户修正自己的行为,所以需要在出错时显示错误信息,以提示用户

register.leaf

1
2
3
4
5
6
7
8
9
#set("content") {
    <h1>#(title)</h1>
    #if(message) {
        <div class="alert alert-danger" role="alert">
            Please fix the following errors:<br />
            #(message)
        </div>
    }
    ...

WebsiteController.swift

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
struct RegisterContext: Encodable {
    let title = "Register"
    let message: String?

    init(message: String? = nil) {
        self.message = message
    }
}
...
    func registerHandler(_ req: Request) throws -> Future<View> {
        let context: RegisterContext
        if let message = req.query[String.self, at: "message"] {
            context = RegisterContext(message: message)
        } else {
            context = RegisterContext()
        }
        ...
    }

 func registerPostHandler(_ req: Request, data: RegisterData) throws -> Future<Response> {
        do {
            try data.validate()
        } catch(let error) {
            let redirect: String
            if let error = error as? ValidationError,
                let message = error.reason.addingPercentEncoding(
                    withAllowedCharacters: .urlQueryAllowed) {
                redirect = "/register?message=\(message)"
            } else {
                redirect = "/register?message=Unknown+error"
            }
            return req.future(req.redirect(to: redirect))
        }
        ...
        }
    }

register validation