Как сделать синхронные URL-запросы с помощью swift 3

Я знаю, что вопрос задавался ранее, и я согласен с большинством ответов, которые утверждают, что лучше следовать тому, как запросы выполняются асинхронно с URLSession в Swift 3. У меня есть следующий сценарий, где асинхронный запрос не может быть использован.

С Swift 3 и возможностью запуска swift на серверах у меня возникает следующая проблема.

  1. Сервер получает запрос от клиента
  2. Для обработки запроса сервер должен отправить URL-запрос и дождаться ответа.
  3. После получения ответа обработайте его и ответьте клиенту

Проблема возникает на шаге 2, где URLSession дает нам возможность инициировать только асинхронную задачу данных. Большинство (если не все) серверные быстрые веб-фреймворки не поддерживают асинхронные ответы. Когда запрос поступает на сервер, все должно быть сделано синхронно, и в конце отправьте ответ.

Единственное решение, которое я нашел до сих пор, - это использование DispatchSemaphore (см. Пример в конце), и я не уверен, будет ли это работать в масштабируемой среде.

Любая помощь или мысли будут оценены.

extension URLSession {
    func synchronousDataTaskWithURL(_ url: URL) -> (Data?, URLResponse?, Error?) {
        var data: Data?
        var response: URLResponse?
        var error: Error?

        let sem = DispatchSemaphore(value: 0)

        let task = self.dataTask(with: url as URL, completionHandler: {
            data = $0
            response = $1
            error = $2 as Error?
            sem.signal()
        })

        task.resume()

        let result = sem.wait(timeout: DispatchTime.distantFuture)
        switch result {
        case .success:
            return (data, response, error)
        case .timedOut:
            let error = URLSessionError(kind: URLSessionError.ErrorKind.timeout)
            return (data, response, error)

        }
    }
}

У меня есть только опыт работы с веб-фреймворком k itura, и именно здесь я столкнулся с проблемой. Я предполагаю, что подобные проблемы существуют во всех других быстрых веб-фреймворках.

2 ответа

Решение

Ваша трехэтапная проблема может быть решена с помощью обработчика завершения, то есть обработчика обратного вызова в соответствии с соглашением Node.js.

import Foundation
import Kitura
import HeliumLogger
import LoggerAPI

let session = URLSession(configuration: URLSessionConfiguration.default)

Log.logger = HeliumLogger()

let router = Router()

router.get("/test") { req, res, next in
    let datatask = session.dataTask(with: URL(string: "http://www.example.com")!) { data, urlResponse, error in
        try! res.send(data: data!).end()
    }

    datatask.resume()
}

Kitura.addHTTPServer(onPort: 3000, with: router)
Kitura.run()

Это краткое демонстрационное решение вашей проблемы, и оно ни в коем случае не следует лучшим практикам Swift/Kitura. Но с помощью обработчика завершения я могу заставить мое приложение Kitura выполнить HTTP-вызов для извлечения ресурса по адресу http://www.example.com, дождитесь ответа, а затем отправьте результат обратно клиенту моего приложения.

Ссылка на соответствующий API: https://developer.apple.com/reference/foundation/urlsession/1410330-datatask

В Vapor вы можете использовать клиент Droplet для выполнения синхронных запросов.

let res = try drop.client.get("https://httpbin.org")
print(res)

Кроме того, вы можете использовать Portal Класс, чтобы сделать синхронные задачи синхронными.

let res = try Portal.open { portal in
    asyncClient.get("https://httpbin.org") { res in
        portal.close(with: res)
    }
}
Другие вопросы по тегам