蒸气3-尝试用数据库中不存在的模型映射登录请求

时间:2019-05-21 20:39:52

标签: swift database vapor

我正在尝试实现一个简单的登录请求API。 如下面的代码示例所示,我有LoginRequest,这是我从客户端(iOS,Android等)获取的数据。这样,我检查数据库中是否存在User,然后检查用户密码是否正确。

但是,我苦苦挣扎的是如何返回数据库中不存在的模型。在下面的示例中,我有LoginResponse。我不想向客户端透露完整的UserAuthToken(数据库中的模型)数据,因此我创建了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,我该如何实现呢?

1 个答案:

答案 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
            }
        }
    }
}

看起来很干净,不需要在客户端解析其他枚举。