I have a tried and tested use of NSURLRequest (and accompaniments) implementation, that works great for GETs, and POSTs for a given URL.
However, I want to now move the target of the URL without changing the URL used by the app, so I'm intending to use a webhop redirect via my DNS provider.
This works fine for the GET requests, but the POSTs just hang... no connection response is received.
The relevant iOS method for handling a redirect is,
-(NSURLRequest *)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)redirectResponse
According to Apple's documentation for (handling redirects),
If the delegate doesn't implement connection:willSendRequest:redirectResponse:, all canonical changes and server redirects are allowed.
Well, that's not my experience, because leaving this method out does not work for me. The request just hangs without a response.
Apple also suggests an implementation, of willSendRequest (see above linked Apple documentation), again this doesn't work for me. I see the invocations, but the resulting requests just hang.
My current implementation of willSendRequest is as follows (see below). This follows the redirect, but the handles the request as if it was a GET, rather than a POST.
I believe the problem is that the redirection is losing the fact that the HTTP request is a POST (there may be more problems, such as carrying the request Body forward too?).
I'm not sure what I should be doing here. So any advice on how to correctly handle a POST that receives a redirect would be appreciated. Thanks.
-(NSURLRequest *)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)redirectResponse
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) redirectResponse;
int statusCode = [httpResponse statusCode];
NSLog (@"HTTP status %d", statusCode);
// http statuscodes between 300 & 400 is a redirect ...
if (httpResponse && statusCode >= 300 && statusCode < 400)
{
NSLog(@"willSendRequest (from %@ to %@)", redirectResponse.URL, request.URL);
}
if (redirectResponse)
{
NSMutableURLRequest *newRequest = [request mutableCopy]; // original request
[newRequest setURL: [request URL]];
NSLog (@"redirected");
return newRequest;
}
else
{
NSLog (@"original");
return request;
}
}
ADDITIONAL INFORMATION 1
The HTTP code received by willSendRequest is 301 - 'Moved Permanently.
Using allHTTPHeaderFields to extract the header fields, I see that he request I originally submit has the header
HTTP header {
"Content-Length" = 244;
"Content-Type" = "application/json";
}
...and the copied / redirected request has the header,
Redirect HTTP header {
Accept = "*/*";
"Accept-Encoding" = "gzip, deflate";
"Accept-Language" = "en-us";
"Content-Type" = "application/json";
}
...which doesn't look like a copy of the original request, or even a superset.
Keep your original request, then provide your own willSendRequest:redirectResponse:
to customize that request, rather than working with the one Apple provides you.
- (NSURLRequest *)connection: (NSURLConnection *)connection
willSendRequest: (NSURLRequest *)request
redirectResponse: (NSURLResponse *)redirectResponse;
{
if (redirectResponse) {
// The request you initialized the connection with should be kept as
// _originalRequest.
// Instead of trying to merge the pieces of _originalRequest into Cocoa
// touch's proposed redirect request, we make a mutable copy of the
// original request, change the URL to match that of the proposed
// request, and return it as the request to use.
//
NSMutableURLRequest *r = [_originalRequest mutableCopy];
[r setURL: [request URL]];
return r;
} else {
return request;
}
}
By doing this, you're explicitly ignoring some aspects of the HTTP spec: Redirects should generally be turned into GET requests (depending on the HTTP status code). But in practice, this behaviour will serve you better when POSTing from an iOS application.
See also: