You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Async-Await在Vapor-Fluent中实现CRUD函数的问题(Swift 5.6)

在Vapor中使用async-await实现CRUD时的Token删除方法错误排查

我正在尝试理解如何在Vapor中创建CRUD函数,发现first()方法返回EventLoopFuture<Token?>,无法作为可选类型解包。

Token.swift代码

import Foundation
import Fluent
import Vapor

final class Token: Model {
  // 1
  static let schema = "tokens"

  // 2
  @ID(key: .id)
  var id: UUID?

  // 3
  @Field(key: "token")
  var token: String

  @Field(key: "debug")
  var debug: Bool

  // 4
  init() { }

  init(token: String, debug: Bool) {
    self.token = token
    self.debug = debug
  }
}

TokenController.swift代码

import Foundation
import Fluent
import Vapor

struct TokenController {
    func create(req: Request) async throws -> HTTPStatus {
        let token = try req.content.decode(Token.self)
        try await token.create(on: req.db)
        return .created
    }
    
    func delete(req: Request) async throws -> HTTPStatus {
        guard let token = req.parameters.get("token") else {
            return .badRequest
        }
        
        try await Token.query(on: req.db)
            .filter(\.$token == token)
            .first()
        
        try await delete(req: req.db)

        return .noContent
    }

}

@available(macOS 12, *)
extension TokenController: RouteCollection {
    func boot(routes: RoutesBuilder) throws {
        let tokens = routes.grouped("token")
        tokens.post(use: create)
        tokens.delete(":token", use: delete)
    }
}

遇到的错误

  • Initializer for conditional binding must have Optional type, not 'EventLoopFuture<Token?>'
  • 尝试按教程方案改写后新增错误:Value of type 'EventLoopFuture<Token?>' has no member 'delete'

我明确使用了async-await,按文档理解底层的EventLoopFuture<>不应直接处理,但实际操作中还是遇到了这些问题,查过文档和教程步骤仍未解决,请问忽略了什么?


问题根源与解决方案

问题1:未正确获取查询结果

你调用Token.query(...).first()后,没有用try await拿到实际的Token?值,直接操作的话拿到的是EventLoopFuture<Token?>对象,自然无法当作可选类型解包。

问题2:错误的删除调用

你写的try await delete(req: req.db)是无效的,这不是删除数据库记录的正确方式,必须通过查询到的Token实例调用delete(on:)方法,或者使用批量删除查询。

修正后的delete方法

func delete(req: Request) async throws -> HTTPStatus {
    guard let tokenString = req.parameters.get("token") else {
        return .badRequest
    }
    
    // 用try await获取实际的Token?实例
    guard let token = try await Token.query(on: req.db)
        .filter(\.$token == tokenString)
        .first() else {
        // 若Token不存在,返回404更符合REST规范
        return .notFound
    }
    
    // 调用实例的delete方法删除记录
    try await token.delete(on: req.db)
    
    return .noContent
}

额外注意点

  • 使用async-await时,所有Fluent的异步方法(返回EventLoopFuture)都必须用try await包裹,才能拿到实际返回值,避免直接操作EventLoopFuture
  • 当查询结果可能为空时,用guard let解包可选值,为空时返回404 Not Found比直接返回204 No Content更合理
  • 也可以用批量删除的方式简化代码(无需先查询实例):
    try await Token.query(on: req.db)
        .filter(\.$token == tokenString)
        .delete()
    
    这种方式会直接删除匹配的记录,不管是否存在,都可以返回204 No Content

内容的提问来源于stack exchange,提问作者somethingsomethingswift

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 10:56:06