Параметр даты прохождения пара

#swift #date #vapor

#swift #Дата #vapor

Вопрос:

В моем приложении 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) это не отвечает на мой вопрос при использовании API вне Postman и 2) это не позволяет мне вводить дату, отличную от сейчас.

Комментарии:

1. Похоже, что 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….

2. Hm 2001 еще более странный. Как насчет тела JSON? @imike

3. Я только что проверил это и ответил ниже, потому что фрагменты кода не поддерживаются в комментариях 🙂

Ответ №1:

Итак, проблема здесь в том, что по умолчанию Date экземпляр декодируется с использованием временного интервала с 1 января 2001 года. Декодер формы URL, который использует Vapor, не поддерживает другие стратегии даты, как JSONDecoder это делает на данный момент, поэтому вам придется выполнять декодирование другим способом. Вот пара идей, которые я мог бы предложить:

  • Просто отправьте временную метку в запросе. Для тестирования разных дат в Postman вы можете установить переменную среды в сценарии предварительного запроса и получить к ней доступ в теле запроса.
  • Вручную реализуйте методы Event.init(from:) и .encode(to:) . Просто чтобы убедиться, что вы не нарушаете свободное кодирование, вам, вероятно, придется добавить некоторую дополнительную логику, но это должно сработать. Вот пример:

     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)   
            }
        }
    }
      

Комментарии:

1. Спасибо за ваш ответ. Я думаю, я выберу средство форматирования даты и строковое решение.

2. @LinusGeffarth Очевидно, что способ, которым я создал средство форматирования даты, не является потокобезопасным. Вы захотите создать средство форматирования в NIO ThreadSpecificVariable .

Ответ №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)
    }
}
  

Комментарии:

1. Думаю, я собираюсь использовать решение для форматирования даты. На первый взгляд кажется немного дружелюбнее… 😄 Спасибо!

2. Также вы могли бы использовать расширение, пожалуйста, взгляните на обновленный ответ 🙂

Ответ №3:

Я понял, что Date объект возвращается в следующем формате при запросе:

 2021-12-31T14:29:00Z
  

Итак, это то, что я пытался передать, и это сработало! Нет необходимости в каком-либо пользовательском декодировании.