Vapor: объект Date декодируется в строковом формате, но декодер ожидает двойной - PullRequest
2 голосов
/ 27 мая 2020

У меня есть модель данных, которая выглядит следующим образом:

struct Post: Content, MySQLModel, Migration, Equatable {
    var id: Int?
    var userId: Int
    var title: String
    var body: String
    var creationDate: Date?
    var lastEditDate: Date?

    static func prepare(on connection: MySQLConnection) -> Future<Void> {
        return MySQLDatabase.create(self, on: connection) { builder in
            builder.field(for: \.id, isIdentifier: true)
            builder.field(for: \.userId)
            builder.field(for: \.title)
            builder.field(for: \.body, type: .text())
            builder.field(for: \.creationDate)
            builder.field(for: \.lastEditDate)
        }
    }
}

И если у меня есть несколько экземпляров в базе данных, я могу безопасно сделать запрос, передав идентификатор сообщения в пути, и я получит объект с датой создания / последнего редактирования, отформатированной в строковом формате:

func retrievePost(on req: Request) throws -> Future<Post> {
    let id = try req.parameters.next(Int.self)

    return Post.find(id, on: req).map(to: Post.self) { post in
        guard let post = post else {
            throw Abort(.notFound)
        }

        return post
    }
}

Если я сделаю запрос GET, в теле ответа я получу следующее:

{
    "body": "value",
    "id": 8723,
    "title": "value",
    "creationDate": "2020-05-27T15:24:41Z",
    "userId": 0
}

И это моя реализация метода PUT:

func updatePost(on req: Request) throws -> Future<Post> {
    var updatedPost = try req.content.syncDecode(Post.self)

    guard let id = updatedPost.id else {
        throw Abort(.badRequest)
    }

    return Post.find(id, on: req).flatMap { post in
        guard let _ = post else {
            throw Abort(.notFound)
        }

        return updatedPost.save(on: req)
    }
}

Но если я отправлю запрос PUT, передав те же самые поля, что и в теле ответа GET, с датой создания, отформатированной как строка, Я получаю эту ошибку:

{
    "error": true,
    "reason": "Could not convert to `Double`: str(\"2020-05-27T15:24:41Z\")"
}

Ожидается двойной. Если я попытаюсь отправить количество секунд после 1970 года, это сработает, но я не понимаю, почему один и тот же объект даты кодируется с использованием строковой даты и декодируется с использованием двойного. Как go обойти эту проблему?

1 Ответ

1 голос
/ 28 мая 2020

Точно так же, как вы, я использую:

struct MyForm {
    let myDate:Date
}

В форме .leaf я использую другое имя поля, чтобы пользователь мог изменять дату:

<input type="date" name="userDate" value="#date(myDate, "yyyy-MM-dd")">

Затем, в событии onclick кнопки отправки, я использую javascript для вычисления значения временной метки для даты, и это возвращается в поле «исходное»:

var d = new Date(f["userDate"].value);
f['myDate'].value = d.getTime()/1000;
return true;

decode, то есть доставляя вам проблемы, теперь должно работать нормально.

Я также использую customTag для представления даты в более коротком формате:

struct DateTag:LeafTag
{
    public func render(_ context:LeafContext) throws -> LeafData
    {
        try context.requireParameterCount(2)
        guard let timestamp = context.parameters.first?.double else { return .string("") }
        guard let format = context.parameters[1].string else { throw "DateTag needs a format to work on" }
        let df = DateFormatter()
        df.dateFormat = format
        return .string(df.string(from:Date(timeIntervalSince1970:timestamp)))
    }
}

Вложенные двойные кавычки допустимы, поскольку Leaf работает только с внутренний набор, оставляя внешний набор для браузера.

...