Как работает request.parameters.next в механизме Vapor?

При использовании Vapor для создания API всегда используйте метод для получения объекта,

request.parameters.next(type.self)

Кажется, это метод извлечения параметра URL, почему он возвращает результат из базы данных? Не удалось найти никаких подсказок в исходном коде фреймворка. Как я могу найти ответ? Спасибо.


person A.C    schedule 13.07.2019    source источник
comment
Чтобы понять это, взгляните на Parameter протокол. Когда вы согласовываете свою модель базы данных с Parameter, вы можете вернуть Future с этим элементом из базы данных. Поэтому в своих запросах вы можете получить объект из БД просто по его ID в параметрах URL.   -  person imike    schedule 13.07.2019
comment
@imike Следуйте вашей мысли, должен ли я найти базу данных (я использую postgreSQL), которая соответствует протоколу параметров, и это поможет мне узнать, как она извлекает из нее данные?   -  person A.C    schedule 13.07.2019
comment
На 99% у вас уже есть User модель в вашем текущем проекте Vapor. Если это так, то просто добавьте extension User: Parameter {}, чтобы согласовать эту модель с Parameter, а затем попробуйте использовать ее в какой-либо конечной точке в вашем контроллере, например router.get("test", User.parameter) { try $0.parameters.next(User.self) }, затем попробуйте вызвать ее следующим образом: localhost:8080/test/‹USER_ID  -  person imike    schedule 13.07.2019
comment
Кстати, не стесняйтесь найти меня в Discord как imike#3049 :)   -  person imike    schedule 13.07.2019


Ответы (1)


Так выглядит протокол Parameter

public protocol Parameter {
    associatedtype ResolvedParameter

    static var routingSlug: String { get }

    static func resolveParameter(_ parameter: String, on container: Container) throws -> ResolvedParameter
}

когда вы что-либо соответствуете Parameter, вы должны реализовать эту функцию

static func resolveParameter(_ parameter: String, on container: Container) throws -> ResolvedParameter

который должен анализировать строковый параметр из URL и возвращать в результате некоторый объект. И поскольку Parameter является общим, вы можете вернуть любой тип, который хотите.

Давайте напишем расширение, чтобы соответствовать Date до Parameter

extension Date: Parameter {
    public static func resolveParameter(_ parameter: String, on container: Container) throws -> Date {
        guard let timeIntervalSince1970 = TimeInterval(parameter) else {
            throw Abort(.notAcceptable, reason: "Unable to parse \"\(parameter)\" parameter")
        }
        return Date(timeIntervalSince1970: timeIntervalSince1970)
    }
}

Метод Parameter анализирует параметр String из URL и возвращает Date, но вы также можете вернуть что-то еще, например. TimeInterval, так как Parameter является общим протоколом

extension Date: Parameter {
    public static func resolveParameter(_ parameter: String, on container: Container) throws -> TimeInterval {
        guard let timeIntervalSince1970 = TimeInterval(parameter) else {
            throw Abort(.notAcceptable, reason: "Unable to parse \"\(parameter)\" parameter")
        }
        return timeIntervalSince1970
    }
}

Когда вы согласовываете свою модель Fluent с Parameter, в ее расширениях уже есть реализация протокола Parameter, поэтому вам не следует объявлять функцию resolveParameter вручную.

Давайте посмотрим, как это работает, если вы объявите метод resolveParameter вручную, например, в User модель

extension User {
    public static func resolveParameter(_ parameter: String, on container: Container) throws -> Future<User> {
        // e.g. User's primary key is UUID
        guard let id = UUID(parameter) else {
            throw Abort(.notAcceptable, reason: "Unable to parse \"\(parameter)\" into UUID")
        }
        // getting database connection from pool
        return container.requestPooledConnection(to: .psql).flatMap { conn in
            // querying user by provided primary key
            return User.query(on: conn)
                       .filter(\.id == id)
                       .first()
                       .unwrap(or: Abort(.notFound, reason: "Unable to find user by provided primary key"))
                       .always {
                // will release connection in any case
                try? container.releasePooledConnection(conn, to: .psql)
            }
        }
    }
}

Итак, как видите, вы можете реализовать resolveParameter, чтобы вернуть все, что захотите.

person imike    schedule 13.07.2019