Response data
I download pictures and save it on my server as data. Is there an easier way to get data from the response? in the previous version there was a simple command for this, I don't understand why it was necessary to complicate everything response.http.body.data
`import Vapor
class PictureManager {
static func saveImage(req: Request, externalUrl: String, name: String) throws -> EventLoopFuture<String> {
var urlString = externalUrl
if !urlString.contains("http") {
urlString = (baseUrl + externalUrl)
}
return req.client.get("\(urlString)").flatMap { (response) in
guard var body = response.body, let data = body.readData(length: body.readableBytes) else {
return req.eventLoop.makeFailedFuture(Abort(.badRequest))
}
let imageData = ImageData(name: name, data: data, id: UUID())
return imageData.save(on: req.db).flatMap({ req.eventLoop.tryFuture({ try imageData.url() }) })
}
}
}`
Do you like it better like this?
import NIOFoundationCompat
[...]
guard let data = response.body.map({ Data(buffer: $0) }) else {
return req.eventLoop.makeFailedFuture(Abort(.badRequest))
}
...
@MaximGolovlev does this help? Can we close this?
Not the OP, but I'm at a loss on how to use the provided solution / workaround (?). What's req and why use it to post an Abort on it? Continuing on the example from the readme, how do I get the response body where it says // handle response?
import AsyncHTTPClient
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
httpClient.get(url: "https://swift.org").whenComplete { result in
switch result {
case .failure(let error):
// process error
case .success(let response):
if response.status == .ok {
// handle response
} else {
// handle remote error
}
}
}
@Bouke I think @weissi answer was in the context of how to use AsyncHTTPClient in the context of Vapor. In Vapor a req is an incoming http request. The http request exposes an instance of AsyncHTTPClient on it req.client. In the above example the user tries to make a call to another downstream service. If this call failed, the original incoming http request shall be aborted with the status code .badRequest.
Based on the default example you would get access to the response's data like so:
import AsyncHTTPClient
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
httpClient.get(url: "https://swift.org").whenComplete { result in
switch result {
case .failure(let error):
// process error
case .success(let response):
if response.status == .ok {
// handle response
guard let bodyAsByteBuffer = response.body else {
// response has no body
}
let bodyAsData = Data(bodyAsByteBuffer)
} else {
// handle remote error
}
}
}
Does that help?
I know this is an older thread, but I can't seem to find a single other question or answer out there that matches. I'm not sure if the APIs have changed, but today (using async-http-client 1.17.0), response.body is of type HTTPClientResponse.Body, not a single ByteBuffer which can be passed into the Data(buffer:) init from NIOFoundationCompat.
There does appear to be the collect(upTo:) method on HTTPClientResponse.Body which returns a ByteBuffer?, but its not clear to me how I would know the expected response length (and the convenience method for it on HTTPClientResponse is internal so I can't use that, but I guess I could reimplement it).
If I have to roll it myself I can but this feels like a gap to me - either in my understanding of how to do this or in the general Foundation compatibility
upTo is not supposed to be the expected response length, it's a guard against you being DoS'd with a massive response. Put the maximum number of bytes you're willing to use for a single request body into the upTo parameter.
Note that you're using the new APIs, which default to streaming first.
Got it, thanks! It was not obvious to me that this is more of a failsafe value than the actual expected content length.