-
Notifications
You must be signed in to change notification settings - Fork 21
Response content handling
Out of the box, hotpotato can automatically convert responses to strings, JSON or images, as well as pipe the data directly to an output stream or download to a file.
The response handling behavior for each request can be configured by assigning a response content handler to each request instance (via responseContentHandler
property).
If the request has no handler configured when it is executed, it will discard the response body. Otherwise, the result will be available at the content
property of the resulting BBHTTPResponse
.
Sadly, not all requests end well. If you're trying to retrieve a JSON resource and the server responds with a 400 response code, the response body, if available, should not be parsed; and by default, it isn't. All the response content handlers available with hotpotato inherit behavior from a selective response content handler — BBHTTPSelectiveDiscarder
.
This handler (and by consequence, all its subclasses) has two properties you can use to decide whether the content should be accepted and parsed:
-
acceptableResponses
: array ofNSNumber
, contains the response codes that hotpotato will consider acceptable for decoding to happen automatically. When set to an empty array (ornil
), any response code will be accepted. -
acceptableContentTypes
: array ofNSString
, contains the content types (value of theContent-Type
header of the response) that hotpotato will consider acceptable for decoding to happen automatically. When set to an empty array (ornil
), any content type will be accepted. When the response has noContent-Type
header (or content), conversion will fail.
Note: Some implementations have static methods to affect defaults:
setDefaultAcceptableResponses:
setDefaultAcceptableContentTypes:
These will affect all newly created handler instances. Might come in handy if the defaults don't suit your needs.
As to actually handling the content, this semi-abstract implementation merely discards everything it receives.
-
Implementation:
BBHTTPSelectiveDiscarder
- Description: Ignores response content.
- Acceptable response codes: 200, 201, 202, 203.
- Acceptable content types: anything.
-
On success, object at
response.content
: Alwaysnil
.
You can call the method discardResponseContent
on the request to have hotpotato set this behavior for you.
Given that this is a completely stateless implementation, if you ever need to use it directly, please consider using the singleton (sharedDiscarder
class method).
-
Implementation:
BBHTTPAccumulator
, extendsBBHTTPSelectiveDiscarder
. -
Description: Accumulates all the data received for the response body and makes it available as a
NSData
. - Acceptable response codes: 200, 201, 202, 203.
- Acceptable content types: anything.
-
On success, object at
response.content
:NSData
This is the default behavior for all requests so you don't have to do any setup. Still, if you explicitly wish to do so you can call the method downloadContentAsData
on the request to have hotpotato set this behavior for you. You can also use the fluent/chained syntax by calling the asData
method.
-
Implementation:
BBHTTPToStringConverter
, extendsBBHTTPAccumulator
. -
Description: Converts all the data received for the response body and creates a new
NSString
with UTF-8 encoding. - Acceptable response codes: 200, 201, 202, 203.
- Acceptable content types: anything.
-
On success, object at
response.content
:NSString
You can call the method downloadContentAsString
on the request to have hotpotato set this behavior for you. You can also use the fluent/chained syntax by calling the asString
method.
-
Description: Converts the response body data to a JSON object. If the JSON object is a
NSDictionary
, it wraps that dictionary withBBJSONDictionary
, which allows the keyed subscript operator to be used as an alias forvalueForKeyPath:
(the default would bevalueForKey:
). -
Implementation:
BBJSONParser
(extendsBBHTTPAccumulator
) - Acceptable response codes: 200, 201, 202, 203.
-
Acceptable content types:
application/json
. -
On success, object at
response.content
: ANSArray
or aBBJSONDictionary
.
You can call the method downloadContentAsJSON
on the request to have hotpotato set this behavior for you. You can also use the fluent/chained syntax by calling the asJSON
method.
-
Implementation:
BBHTTPImageDecoder
, extendsBBHTTPAccumulator
. -
Description: Create a
UIImage
(NSImage
on OSX) using the contents of the response body. - Acceptable response codes: 200, 201, 202, 203.
-
Acceptable content types:
image/*
. -
On success, object at
response.content
:UIImage
on iOS,NSImage
on OSX.
You can call the method downloadContentAsImage
on the request to have hotpotato set this behavior for you. You can also use the fluent/chained syntax by calling the asImage
method.
-
Implementation:
BBHTTPStreamWriter
, extendsBBHTTPSelectiveDiscarder
. - Description: Pipes all bytes received, as they are received, to an output stream.
- Acceptable response codes: 200, 201, 202, 203.
- Acceptable content types: anything.
-
On success, object at
response.content
: Alwaysnil
.
You can call the method downloadToStream:
and pass a NSOutputStream
to have hotpotato set this behavior for you.
-
Implementation:
BBHTTPFileWriter
, extendsBBHTTPSelectiveDiscarder
. - Description: Streams the download to disk, writing bytes as they arrive — thus keeping memory usage to a minimum. If something goes wrong while downloading the file, the partial file is automatically cleaned up.
- Acceptable response codes: 200, 201, 202, 203.
- Acceptable content types: anything.
-
On success, object at
response.content
: Alwaysnil
.
You can call the method downloadToFile:
and pass a NSString
with the target location for the file, to have hotpotato set this behavior for you.
As helpful as all the available behaviors may be, there will be cases where you need to tailor response handling to your specific needs.
If this is the case, you'll need to implement the protocol BBHTTPContentHandler
and then supply an instance of your implementation to the request, via the responseContentHandler
property.
Content handlers will be progressively fed with response body data and, when the response is finished, their parseContent:
method will be called. This means you can adopt a progressive decoding approach and have parseContent:
merely finish the process or accumulate data and then convert it when parseContent:
is called.
- If your handler converts JSON to a domain object, consider subclassing
BBJSONParser
; - If your handler first accumulates data into a buffer and then converts that buffer to a resulting object, consider subclassing
BBHTTPAccumulator
; - If your handler should only attempt to handle responses with certain response status codes or with certain content types, consider subclassing
BBHTTPSelectiveDiscarder
.
Also, don't forget to implement the optional cleanup
method if your handler requires cleanup after either a successful or error response.
If something goes wrong while performing the final conversion — triggered when hotpotato calls parseContent:
— you must set the error
method argument:
if (somethingWentWrong) *error = [NSError errorWith...];
Tip: You don't need to check whether
error
isNULL
because hotpotato will always pass a valid address for you to set.
If the conversion involves multiple steps and it fails midway, when appropriate, you should return the intermediate object. For instance, when parsing JSON, BBJSONParser
first closes the stream where it received all the data and obtains a NSData
, which it then converts to a JSON object. If the conversion to a JSON object fails, it sets the appropriate error and returns the intermediate NSData
— it may be useful for debug.