蒸汽通过日期参数

Vapor pass date parameter

在我的 Vapor 3 应用程序中,我有一个 Event 模型,它具有 startDate: DateendDate: Date.
属性 现在,我想知道如何在 POST 请求中传递这些日期值。在 Postman 中,我在 x-www-form-urlencoded 中尝试了以下操作:

startDate -> 2019-03-14

这returns下面的错误:

Could not convert to Double: str(\"2019-03-14\")

显然,Date 变成了 Double
那么,相反,我需要传递什么值?


备注

我知道,在 Postman 中,我可以插入 {{$timestamp}},但是 1) 在 Postman 外部使用 API 时,这不能回答我的问题;2) 这不允许我要输入 现在 .

以外的日期

我不确定 x-www-form-urlencoded,因为我测试了它,如果我发送一个 0 的日期,它会将它解码为 2001-01-01 00:00:00 +0000,我认为它肯定应该是 1970-01-01 00:00:00 +0000.

但是使用 JSON 有效载荷,您可以灵活地提供一个 JSONDecoder 根据需要配置的。

struct Payload: Content {
    var date: Date
}

如果您想将日期作为 UNIX 时间戳发送

router.post("check") { req throws -> Future<String> in
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .secondsSince1970 // choose it for unix-timestamp
    return try req.content.decode(json: Payload.self, using: decoder).map { p in
        return String(describing: p.date)
    }
}

如果您想以自己的格式发送日期

router.post("check") { req throws -> Future<String> in
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    formatter.timeZone = TimeZone(secondsFromGMT: 0)
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .formatted(formatter) // custom date formatter
    return try req.content.decode(json: Payload.self, using: decoder).map { p in
        return String(describing: p.date)
    }
}

因此,对于 unix-timestamp,您应该发送从 1970 年开始的秒数,例如0 将被解码为 1970-01-01 00:00:00 +0000.

对于上述自定义格式,您应该发送 2018-01-01 00:00:00 之类的日期以将其解码为 2018-01-01 00:00:00 +0000

UPD:你可以写一个扩展来漂亮地解码它

extension ContentContainer where M: Request {
    func decodeJson<D>(_ payload: D.Type) throws -> Future<D> where D: Decodable {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .formatted(formatter)
        return try decode(json: payload, using: decoder)
    }
}

这样你就可以像这样解码你的有效载荷

router.post("check") { (req) throws -> Future<String> in
    return try req.content.decodeJson(Payload.self).map { p in
        return String(describing: p.date)
    }
}

所以这里的问题是,默认情况下,Date 实例使用自 2001 年 1 月 1 日以来的时间间隔进行解码。Vapor 使用的 URL form decoder 不支持不同的日期策略,例如JSONDecoder 目前确实如此,因此您必须以不同的方式进行解码。以下是我可以提出的一些想法:

  • 只需在请求中发送时间戳即可。为了在 Postman 中测试不同的日期,您可以在预请求脚本中设置环境变量并在请求正文中访问它。
  • 手动实现 Event.init(from:).encode(to:) 方法。只是为了确保您不会破坏 Fluent 编码,您可能需要添加一些额外的逻辑,但它应该可以工作。这是一个例子:

    final class Event: Model {
        static let formDateFormatter: DateFormatter = {
            let formatter = DateFormatter()
            formatter.calendar = Calendar(identifier: .iso8601)
            formatter.locale = Locale(identifier: "en_US_POSIX")
            formatter.timeZone = TimeZone(secondsFromGMT: 0)
            formatter.dateFormat = "yyyy-MM-dd"
            return formatter
        }()
    
        var startDate: Date
        var endDate: Date
    
        init(from decoder: Decoder)throws {
            let container = try decoder.container(keyedby: CodingKeys.self)
    
            if let start = try? container.decode(String.self, keyedBy: .startDate), let date = Event.formDateFormatter.string(from: start) {
                self.startDate = date
            } else {
                self.startDate = try container.decode(Date.self, keyedBy: .startDate)   
            }
    
            if let end = try? container.decode(String.self, keyedBy: .endDate), let date = Event.formDateFormatter.string(from: end) {
                self.endDate = date
            } else {
                self.endDate = try container.decode(Date.self, keyedBy: .endDate)   
            }
        }
    }
    

我意识到 Date 对象在查询时以以下格式返回:

2021-12-31T14:29:00Z

所以这就是我试图通过的方法并且有效!无需任何自定义解码。