Vapor 3

编程入门 行业动态 更新时间:2024-10-26 21:35:56
本文介绍了Vapor 3 - 如何在保存对象之前检查类似的电子邮件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我想创建一个路由来让用户更新他们的数据(例如更改他们的电子邮件或用户名).为确保用户不能使用与其他用户相同的用户名,我想检查数据库中是否已存在具有相同用户名的用户.

I would like to create a route to let users update their data (e.g. changing their email or their username). To make sure a user cannot use the same username as another user, I would like to check if a user with the same username already exists in the database.

我已经在迁移中设置了唯一的用户名.

I have already made the username unique in the migrations.

我有一个如下所示的用户模型:

I have a user model that looks like this:

struct User: Content, SQLiteModel, Migration { var id: Int? var username: String var name: String var email: String var password: String var creationDate: Date? // Permissions var staff: Bool = false var superuser: Bool = false init(username: String, name: String, email: String, password: String) { self.username = username self.name = name self.email = email self.password = password self.creationDate = Date() } }

这是我想使用它的一段代码:

This is the piece of code where I want to use it:

func create(_ req: Request) throws -> EventLoopFuture<User> { return try req.content.decode(UserCreationRequest.self).flatMap { userRequest in // Check if `userRequest.email` already exists // If if does -> throw Abort(.badRequest, reason: "Email already in use") // Else -> Go on with creation let digest = try req.make(BCryptDigest.self) let hashedPassword = try digest.hash(userRequest.password) let persistedUser = User(name: userRequest.name, email: userRequest.email, password: hashedPassword) return persistedUser.save(on: req) } }

我可以这样做(请参阅下一个代码段),但这似乎是一个奇怪的选择,因为在进行更多检查时,它需要大量嵌套,例如必须执行唯一性(例如在更新用户的情况下).

I could do it like this (see next snippet) but it seems a strange option as it requires a lot of nesting when more checks for e.g. uniqueness would have to be performed (for instance in the case of updating a user).

func create(_ req: Request) throws -> EventLoopFuture<User> { return try req.content.decode(UserCreationRequest.self).flatMap { userRequest in let userID = userRequest.email return User.query(on: req).filter(\.userID == userID).first().flatMap { existingUser in guard existingUser == nil else { throw Abort(.badRequest, reason: "A user with this email already exists") } let digest = try req.make(BCryptDigest.self) let hashedPassword = try digest.hash(userRequest.password) let persistedUser = User(name: userRequest.name, email: userRequest.email, password: hashedPassword) return persistedUser.save(on: req) } } }

由于其中一个答案建议我尝试添加错误中间件(请参阅下一个片段),但这并没有正确捕获错误(也许我在代码中做错了 - 刚开始使用 Vapor).

As one of the answers suggested I've tried to add Error middleware (see next snippet) but this does not correctly catch the error (maybe I am doing something wrong in the code - just started with Vapor).

import Vapor import FluentSQLite enum InternalError: Error { case emailDuplicate } struct EmailDuplicateErrorMiddleware: Middleware { func respond(to request: Request, chainingTo next: Responder) throws -> EventLoopFuture<Response> { let response: Future<Response> do { response = try next.respond(to: request) } catch is SQLiteError { response = request.eventLoop.newFailedFuture(error: InternalError.emailDuplicate) } return response.catchFlatMap { error in if let response = error as? ResponseEncodable { do { return try response.encode(for: request) } catch { return request.eventLoop.newFailedFuture(error: InternalError.emailDuplicate) } } else { return request.eventLoop.newFailedFuture(error: error) } } } }

推荐答案

我会使用 Migration 使模型中的字段 unique 如:

I would make the field unique in the model using a Migration such as:

extension User: Migration { static func prepare(on connection: SQLiteConnection) -> Future<Void> { return Database.create(self, on: connection) { builder in try addProperties(to: builder) builder.unique(on: \.email) } } }

如果您使用默认的 String 作为 email 的字段类型,那么您将需要减少它,因为这会创建一个字段 VARCHAR(255) 对于 UNIQUE 键来说太大了.然后,我会使用一些自定义 Middleware 来捕获使用同一电子邮件再次尝试保存记录时出现的错误.

If you use a default String as the field type for email, then you will need to reduce it as this creates a field VARCHAR(255) which is too big for a UNIQUE key. I would then use a bit of custom Middleware to trap the error that arises when a second attempt to save a record is made using the same email.

struct DupEmailErrorMiddleware: Middleware { func respond(to request: Request, chainingTo next: Responder) throws -> EventLoopFuture<Response> { let response: Future<Response> do { response = try next.respond(to: request) } catch is MySQLError { // needs a bit more sophistication to check the specific error response = request.eventLoop.newFailedFuture(error: InternalError.dupEmail) } return response.catchFlatMap { error in if let response = error as? ResponseEncodable { do { return try response.encode(for: request) } catch { return request.eventLoop.newFailedFuture(error: InternalError.dupEmail) } } else { return request.eventLoop.newFailedFuture(error: error ) } } } }

您的自定义错误需要类似于:

Your custom error needs to be something like:

enum InternalError: Debuggable, ResponseEncodable { func encode(for request: Request) throws -> EventLoopFuture<Response> { let response = request.response() let eventController = EventController() //TODO make this return to correct view eventController.message = reason return try eventController.index(request).map { html in try response.content.encode(html) return response } } case dupEmail var identifier:String { switch self { case .dupEmail: return "dupEmail" } } var reason:String { switch self { case .dupEmail: return "Email address already used" } } }

在上面的代码中,通过在控制器中设置一个值来向用户显示实际错误,然后在视图中获取该值并显示警报.此方法允许通用错误处理程序负责显示错误消息.但是,在您的情况下,您可能只需在 catchFlatMap 中创建响应.

In the code above, the actual error is displayed to the user by setting a value in the controller, which is then picked up in the view and an alert displayed. This method allows a general-purpose error handler to take care of displaying the error messages. However, in your case, it might be that you could just create the response in the catchFlatMap.

更多推荐

Vapor 3

本文发布于:2023-10-22 23:57:42,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1519117.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:Vapor

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!