Как сделать синхронные URL-запросы с помощью swift 3
Я знаю, что вопрос задавался ранее, и я согласен с большинством ответов, которые утверждают, что лучше следовать тому, как запросы выполняются асинхронно с URLSession в Swift 3. У меня есть следующий сценарий, где асинхронный запрос не может быть использован.
С Swift 3 и возможностью запуска swift на серверах у меня возникает следующая проблема.
- Сервер получает запрос от клиента
- Для обработки запроса сервер должен отправить URL-запрос и дождаться ответа.
- После получения ответа обработайте его и ответьте клиенту
Проблема возникает на шаге 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)
}
}