2

我正在做一个流利的小型蒸汽项目。现在我应该能够创建一个用户,为所述创建的用户创建一个令牌并返回带有会话令牌的用户。之后我应该能够登录并创建一个新令牌。

现在的问题是,每当我创建一个令牌时,id所述令牌的值设置为1,即使我将它设置为nil。创建用户时不会发生这种情况,然后自动增量按预期工作。

我使用的数据库是 MySQL。

我的迁移CreateTokens.swift

import Fluent

struct CreateTokens: Migration {
    func prepare(on database: Database) -> EventLoopFuture<Void> {
        database.schema(Token.schema)
            .field("id", .int, .identifier(auto: true))
            .unique(on: "id")
            .field("user_id", .int, .references("users", "id"))
            .field("value", .string, .required)
            .unique(on: "value")
            .field("created_at", .datetime, .required)
            .field("expires_at", .datetime)
            .create()
    }
    
    func revert(on database: Database) -> EventLoopFuture<Void> {
        database.schema(Token.schema).delete()
    }
}

我的令牌 ( Token.swift):

import Vapor
import Fluent

final class Token: Model {
    static let schema = "tokens"
    
    @ID(custom: "id", generatedBy: .database)
    var id: Int?
    
    @Parent(key: "id")
    var user: User
    
    @Field(key: "value")
    var value: String
    
    @Field(key: "expires_at")
    var expiresAt: Date?
    
    @Timestamp(key: "created_at", on: .create)
    var createdAt: Date?
    
    init() {}
    
    init(id: Int? = nil,
         userId: User.IDValue,
         token: String,
         expiresAt: Date?
    ) {
        self.id = id
        self.$user.id = userId
        self.value = token
        self.expiresAt = expiresAt
    }
}

extension Token: ModelTokenAuthenticatable {
    static let valueKey = \Token.$value
    static let userKey = \Token.$user
    
    var isValid: Bool {
        guard let expiryDate = expiresAt else {
            return true
        }
        
        return expiryDate > Date()
    }
}

在我的用户结构中,当我创建一个令牌时:

    func createToken() throws -> Token {
        let calendar = Calendar(identifier: .gregorian)
        let expiryDate = calendar.date(byAdding: .year, value: 1, to: Date())
        let token = try Token(userId: requireID(), token: [UInt8].random(count: 16).base64, expiresAt: expiryDate)
        return token
    }

如果我在并打印出令牌上设置断点return token,我会得到以下信息:

(lldb) po token
Token(input: [expires_at: Optional(2021-11-11 21:01:10 +0000), value: "wn8kvw/GYSfqL280RLCDbQ==", id: 1])

ID 值设置为1,但是在代码中(对我而言)很清楚我已将其设置为nil. 即使我token.id = nil在返回之前添加,它仍然设置为1.

我在这里做错了什么?还是我偶然发现了一个流利的错误?

4

2 回答 2

3

正如您所提到的,键“id”似乎同时用于 theiduser属性,这是这种字符串类型 API 中常见的错误类型。

我试图缓解这种情况的一种方法是扩展FieldKey并强制自己始终使用这些静态属性而不是字符串,这样 IDE 的自动完成功能让我有机会考虑使用正确的字段键——当您手动键入这些。

extension FieldKey {
  static var userID: FieldKey { "user_id" }
}

// And in the model
@Parent(key: .userID)
var user: User

希望这对将来有帮助!

于 2020-11-11T21:43:00.227 回答
2

我发现了我至少花费了 1.5 小时的问题,这是一个愚蠢的错误。

问题在于:

    @Parent(key: "id")
    var user: User

这应该是user_id关键,而不是id. 现在它将 id 重新写入我发送的任何 userId。

于 2020-11-11T21:31:48.197 回答