【问题标题】:Vapor pass date parameter蒸汽通过日期参数
【发布时间】:2019-03-14 22:23:19
【问题描述】:

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

startDate -> 2019-03-14

这将返回以下错误:

无法转换为Double:str(\"2019-03-14\")

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


注意

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

【问题讨论】:

  • 它看起来像 Content 解码器 x-www-form-urlencoded 期望 TimeInterval aka Double 日期。因此,您可以使用 unix-timestamp 将日期传递给服务器。但我刚刚尝试发布 0 作为日期,Vapor 将其解码为 2001-01-01 00:00:00 +0000,认为它绝对应该是 1970-01-01 00:00:00 +0000。所以它看起来像是某个地方的错误,但我无法在 Vapor 的源代码中找到它......
  • Hm 2001 甚至很奇怪。 JSON 正文怎么样? @imike
  • 我刚刚检查过并在下面回答了因为 cmets 不支持代码 sn-ps :)

标签: swift date vapor


【解决方案1】:

发现Date对象在查询时返回如下格式:

2021-12-31T14:29:00Z

所以这就是我试图通过的并且成功了!无需任何自定义解码。

【讨论】:

    【解决方案2】:

    所以这里的问题是,默认情况下,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)   
              }
          }
      }
      

    【讨论】:

    • 感谢您的回答。我想我会使用日期格式化程序和字符串解决方案。
    • @LinusGeffarth 显然我创建日期格式化程序的方式不是线程安全的。您将需要在 NIO ThreadSpecificVariable 中创建格式化程序。
    【解决方案3】:

    我不确定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)
        }
    }
    

    【讨论】:

    • 猜猜我将使用日期格式化程序解决方案。看起来对眼睛更友好了......?谢谢!
    • 您也可以使用扩展程序,请查看更新后的答案:)
    猜你喜欢
    • 2020-10-17
    • 2018-11-25
    • 2020-05-15
    • 1970-01-01
    • 2014-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多