我正在尝试实现一个简单的登录请求API。
如下面的代码示例所示,我有LoginRequest
,这是我从客户端(iOS,Android等)获取的数据。这样,我检查数据库中是否存在User
,然后检查用户密码是否正确。
但是,我苦苦挣扎的是如何返回数据库中不存在的模型。在下面的示例中,我有LoginResponse
。我不想向客户端透露完整的User
和AuthToken
(数据库中的模型)数据,因此我创建了LoginResponse
模型。如果一切成功,我想向客户端显示的唯一数据是LoginResponse
的{{1}}。
我对用户的期望:用户名和密码
authToken
我给用户的响应:成功(使用authToken),错误的密码或用户不存在。
struct LoginRequest : Codable, Content {
var username: String
var password : String
}
/// Response model for user login.
struct LoginResponse : Codable, Content {
enum LoginResultType : String, Codable {
case success
case incorrectPassword
case noUser
}
var authToken : String?
var state : LoginResultType
init(state : LoginResultType) {
authToken = nil
self.state = state
}
}}
下面的代码令人头疼,在数据库中创建新的authToken后,这不会让我返回LoginResponse。
func login(_ req : Request) throws -> Future<LoginResponse> {
return try req.content.decode(LoginRequest.self).flatMap(to: LoginResponse.self) { loginRequest in
return User.query(on: req).filter(\.username == loginRequest.username)
.first().map(to: LoginResponse.self) { user in
/// check if we have found a user, if not then return LoginResponse with noUser
guard let u = user else {
return LoginResponse(state: .noUser)
}
/// if the password isn't the same, then we tell the user
/// that the password is incorrect.
if u.password != loginRequest.password {
return LoginResponse(state: . incorrectPassword)
}
/// If username and password are the same then we create a random authToken and save this to the DB.
/// Then I need to return LoginResponse with success and authToken.
let authToken = AuthToken(token: "<Random number>")
authToken.user = u.id
return authToken.create(on: req).flatMap(to: LoginResponse.self){ auth in
var lr = LoginResponse(state: .success)
lr.authToken = auth.token
return lr
}
}
}
}
一旦创建了authToken,我想在LoginResponse模型中显示具有成功状态的authToken,我该如何实现呢?
答案 0 :(得分:2)
只需使用map
代替flatMap
。
map
-返回不属于未来的内容
flatMap
-返回未来
此外,您将来还可以使用
返回一些对象req.eventLoop.newSucceededFuture(result: someObject)
下面的代码应该像灵符一样工作
func login(_ req : Request) throws -> Future<LoginResponse> {
return try req.content.decode(LoginRequest.self).flatMap { loginRequest in
return User.query(on: req).filter(\.username == loginRequest.username).first().flatMap { user in
/// check if we have found a user, if not then return LoginResponse with noUser
guard let u = user else {
return req.eventLoop.newSucceededFuture(result: LoginResponse(state: .noUser))
}
/// if the password isn't the same, then we tell the user
/// that the password is incorrect.
if u.password != loginRequest.password {
return req.eventLoop.newSucceededFuture(result: LoginResponse(state: .incorrectPassword))
}
/// If username and password are the same then we create a random authToken and save this to the DB.
/// Then I need to return LoginResponse with success and authToken.
let authToken = AuthToken(token: "<Random number>")
authToken.user = u.id
return authToken.create(on: req).map { auth in
var lr = LoginResponse(state: .success)
lr.authToken = auth.token
return lr
}
}
}
}
但是,老实说,使用200 OK
http代码返回错误是一种不好的做法。
最佳做法是改用HTTP代码来处理错误,而Vapor则是通过设计使用这种方式。
所以您的代码可能看起来像这样
struct LoginResponse: Content {
let authToken: String?
}
func login(_ req : Request) throws -> Future<LoginResponse> {
return try req.content.decode(LoginRequest.self).flatMap { loginRequest in
return User.query(on: req).filter(\.username == loginRequest.username).first().flatMap { user in
/// check if we have found a user, if not then throw 404
guard let u = user else {
throw Abort(.notFound, reason: "User not found")
}
/// if the password isn't the same, then throw 400
if u.password != loginRequest.password {
throw Abort(.badRequest, reason: "Incorrect password")
}
/// If username and password are the same then we create a random authToken and save this to the DB.
/// Then I need to return LoginResponse with success and authToken.
let authToken = AuthToken(token: "<Random number>")
authToken.user = u.id
return authToken.create(on: req).map { auth in
var lr = LoginResponse(state: .success)
lr.authToken = auth.token
return lr
}
}
}
}
看起来很干净,不需要在客户端解析其他枚举。