ASIHTTPRequest GET Request Issue - ios

My request is
request_ = [[ASIFormDataRequest requestWithURL:requestUrl] retain];
[request_ setDelegate:self];
[request_ setRequestMethod:#"GET"];
[request_ setTimeOutSeconds:HTTP_TIME_OUT];
[request_ startAsynchronous];
But the response from the server is
HTTP Status 405 - Request method 'GET' not supported. The specified HTTP method is not allowed for the requested resource (Request method 'GET' not supported).
Please note that the url doesn't have any "GET" parameters along with it even though it is a get request. The thing is I am getting the proper response from the server when simply I take the request URL in a browser or when I call it using "HttpRequester"(a Firefox add-on to test http requests - well I'm sure you know that). What could have went wrong?
Thanks in advance.
Balu

For GET requests, use ASIHTTPRequest.
ASIFormDataRequest is for POST requests.
However, if all you need is a simple GET request, why bother with ASI? You can do this in a dispatch_async block:
dispatch_async(<some_queue>, ^{
NSError * error = nil;
NSString * response = [NSString stringWithContentsOfURL: stringWithContentsOfURL: requestUrl error: &error];
[self performSelectorOnMainThread: #selector(processResult:) withObject: response waitUntilDone: NO];
});

Solved it. It was because of the absence of the header parameter "Accept" in requests that have been made. Once it was added, everything worked like a charm.
Also replaced ASIFormDataRequest with ASIHTTPRequest(that one was a silly mistake).

Seems it is a server side issue. Please verify if all the header parameters as required by the server are there in request.

why you create a ASIFormDataRequest aka POSTRequest and set the RequestMethod to GET?
If you want to make a GET Request use ASIHTTPRequest instead.

Related

How to do a JSON encoded GET request with AFNetworking-2.0?

I'm trying to use a pattern similar to this post which describes doing a PUT/POST for RESTful API, but for a GET.
My original code looks almost the same, except I used a GET: keyword. I quickly discovered that rather than sending a JSON body, it instead url encodes the parameters. This is not mentioned in the documentation of the AFJSONSerializer class. You have to go to the superclass documentation (AFHTTPSerializer) and read through its properties, where you'll find the one about HTTPMethodsEncodingParametersInURI. By default that set is populated with HEAD, GET, and DELETE. So for those types of requests, the JSON serializer apparently reverts to its parent class for behavior?
So I put together the following code:
AFHTTPSessionManager* manager = [AFHTTPSessionManager manager];
manager.securityPolicy.allowInvalidCertificates = YES;
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.requestSerializer.HTTPMethodsEncodingParametersInURI = [NSSet set];
[manager.requestSerializer setAuthorizationHeaderFieldWithUsername: currentUser() password: currentPassword()];
[manager
GET: #"https://172.16.214.214:44321/trees"
parameters: [NSDictionary dictionary]
success:^(NSURLSessionDataTask* task, id responseObject){
NSLog(#"Response: %#", responseObject);}
failure:^(NSURLSessionDataTask* task, NSError* error){
NSLog(#"Error: %#", error);}];
The line that sets HTTPMethodsEncodingParametersInURI = [NSSet set] is intended to let me get JSON encoded parameters like I wanted for the GET too. Unfortunately, I see nothing at the server when I use this and get the following in my error console:
2013-12-10 10:11:14.149 myValve[957:60b] Error: Error Domain=NSURLErrorDomain Code=-1005 "The
network connection was lost." UserInfo=0x17e5d4b0
{NSErrorFailingURLStringKey=https://172.16.214.214:44321/trees,
NSErrorFailingURLKey=https://172.16.214.214:44321/trees, NSLocalizedDescription=The network
connection was lost., NSUnderlyingError=0x17e53240 "The network connection was lost."}
What am I still missing?
The rationale behind AFNetworking's behavior here is probably most concisely explained in the Stack Overflow question, HTTP GET with request body. One of those answers quotes Ray Fielding:
... In other words, any HTTP request message is allowed to contain a message body, and thus must parse messages with that in mind. Server semantics for GET, however, are restricted such that a body, if any, has no semantic meaning to the request. The requirements on parsing are separate from the requirements on method semantics.
So, yes, you can send a body with GET, and no, it is never useful to do so.
The HTTP/1.1 spec defines GET to "retrieve whatever information (in the form of an entity) is identified by the Request-URI" and does not contemplate information to be included in the body of the request. So the use of the body in GET request technically might not be prohibited, but, at best, it is non-standard. AFNetworking's choice not to support it is not entirely surprising.
So, you might not want to put JSON in the body for a GET requests. One would generally add the parameters to the URL. If you want to send JSON in the body of the request, then do a POST.
Sounds like it can't connect to the server, have you tried to make a call outside iOS?
For reference I normally make a JSON request like this:
NSURL *url = #"https://172.16.214.214:44321/trees";
NSData* data = [NSData dataWithContentsOfURL: url];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];

HTTP Post - Time Out - Multiple Request getting initiated within timeout interval

I am using HTTP Post method and initiating a synchronous request.
[NSURLConnection sendSynchronousRequest: ..]
For HTTP POST requests, the default time out is happening at 75 seconds as discussed in many threads.
But during that time out period of 75 seconds, Multiple web service requests are getting initiated for us for the same request raised with all the same parameters.
Please let us know What causes this multiple requests to get initiated? Is this due to HTTP POST in general or because of Synchronous request?
#iOS Sample Code
[body appendData:[[NSString stringWithFormat:#"\r\n--%#--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:body];
[request addValue:[NSString stringWithFormat:#"%d", body.length] forHTTPHeaderField: #"Content-Length"];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
NSURLResponse *response;
response = nil;
urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if(urlData)
{
NSString *responseString = [[NSString alloc] initWithData:urlData encoding:NSASCIIStringEncoding];
[self parseStringInformation:responseString infoDict:informationDictionary];
//NSLog(#"%#",responseString);
}
Without the server's request-response logs there are several possibilities.
Programmer Error: Have you already gone through all of "gotchya" type situations?
Have you put a logging message right before your "urlData = [NSURLConnection sendSynchronousRequest: ..." line to make sure your code is only calling it once?
Are you calling this function from within your main GUI thread, if you are that's not supported/recommended, which means it could be causing side-effects like what you're describing. https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html
Are you sure your Request is set up as a POST and has the proper headers like "Content-type: multipart/form-data, boundary=X"
Web Server Responses: Without the web servers logs (or the code for the service your POSTing to) it's hard to say...
Perhaps your server is sending cyclical redirects to the client. If you don't implement a "connection:willSendRequest" then the redirects could be followed for ?X? amount of times for one request. http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/urlloadingsystem/Articles/RequestChanges.html
API Error: You've found some corner case which is causing unwanted side effects. Maybe apple has an bug tracker or developer support forum?
If this is the case you'll have to work around the bug, until it's fixed. I suggest implementing the Asynchronous call chain. "Loading Data Asynchronously" https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html

NSURLRequest: How to handle a redirected post?

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:
iOS Developer Library, URL Loading System Programming Guide: Handling Redirects and Other Request Changes
The HTTP specification for handling the 3xx class of status codes is very unfriendly towards protocols other than GET and HEAD. It expects some kind of user interaction for at the intermediary step of the redirection, which has lead to a plethora of incompatible client and server implementations, as well as a serious headache for web service developers.
From an iOS NSURL point of view, one of the things you might want to verify is that the original POST body is included in the new, redirect request.
Based off your comments on my original answer, and the edits to your question, it would appear that the URL you are trying to access has been updated permanently (301 status code). In which case you can actually avoid the redirects altogether by using the new URL.

How to handle redirecting urls with get parameters in the request?

I am trying to get an html response from a server(aspx).
NSString *urlstr = [[NSString alloc] initWithFormat:#"http://www.someSiteThatRedirectsWithGetParameters.com?parameter1=1&parameter2=2"];
NSURL *url = [ NSURL URLWithString:urlstr];
NSMutableURLRequest *urlRequest = [ NSURLRequest requestWithURL: url];
NSData data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
NSURL *lastURL=[response URL];
NSLog(#"Last url %#", lastURL);
I am getting lastURL as www.someSiteThatRedirectsWithGetParameters.com and therefore not getting the html page for the original requested url.
Why is this so and how can I solve this?
EDIT
This is all what I have tried so far : Get the last redirected url in iOS 5?
but I am still not able to achieve what I am trying to i.e : get the html page source of the original GET request(going through redirections) having parameters in the url ?
I am getting lastURL as www.someSiteThatRedirectsWithGetParameters.com
and therefore not getting the html page for the original requested
url.
If your request is being redirected, it's because the server to which you're connecting is telling you that there is no page at the URL to which you first made the request, and you should use the redirected URL instead. That's the whole point of a redirect response.
If the response that you get is a HTTP 303, the new request will properly be changed from POST to GET. This is sometimes done to avoid cases where refreshing the page would resubmit the original request with undesirable consequences. In this case, you POST your request to some URL, and the server accepts the request but responds by redirecting the client to a different URL which ultimately displays the server's response.
Why is this so and how can I solve this?
I've explained the 'why' above. The "solution" is to accept the response from the server and follow the redirect to the URL that contains the server's response.
I would try the approach of sending two requests. Send the first request, then the redirectURL will be the response. Then make a new URL of the response, sending a request to that url with the GET-parameters. I haven't tried it, but I suppose it could work.

Google App Engine. Google app engine seeing POST requests as a GET

I'm having the strangest issue right now with google app engine. I'm sending a POST request from iOS and google app engine instead invokes the GET handler.
I've sandboxed this one situation for testing and can't get it figured out. I have an iOS app that just sends a request. And I've commented out everything on GAE except for the service. The service only logs a parameter and returns.
The iOS app I've tried using two different ways of sending the request. Neither works.
iOS Code:
/*
NSURL * url = [NSURL URLWithString:#"http://beermonster-gngrwzrd.appspot.com/TestParameter"];
ASIFormDataRequest * _fdrequest = [[ASIFormDataRequest alloc] initWithURL:url];
[_fdrequest setPostValue:#"hello" forKey:#"testkey"];
[_fdrequest startAsynchronous];
*/
NSURL * __url = [NSURL URLWithString:#"http://beermonster-gngrwzrd.appspot.com/TestParameter"];
NSMutableURLRequest * __request = [NSMutableURLRequest requestWithURL:__url];
[__request setHTTPMethod:#"POST"];
NSString * post = [NSString stringWithFormat:#"testkey=hello"];
[__request setHTTPBody:[post dataUsingEncoding:NSUTF8StringEncoding]];
[NSURLConnection sendSynchronousRequest:__request returningResponse:nil error:nil];
My App engine handler:
class TestParameter(webapp.RequestHandler):
def post(self):
logging.debug(self.request.get("testkey"))
self.response.out.write(self.request.get("testkey"))
print self.request.get("testkey")
def get(self):
logging.debug("get")
logging.debug(self.request.get("testkey"))
self.response.out.write(self.request.get("testkey"))
The output in the GAE logs shows the "get" code path which isn't correct.
Any ideas why POST requests would come into GAE as a GET? Is there some configuration in GAE that I missed?
Thanks!
Check the entry in app.yaml for the script that handles "/TestParameter". Does it specify "secure: always"? If it does and you make a non-secure connection you will get a 302 redirecting to the secure version.
To fix this either make your post over HTTPS or remove "secure: always" from the entry in app.yaml.
From what I can tell if you want to send POST requests to GAE. Make sure you do it on https. If you make the request on a non-https attempt, it sends back a 302 redirect to the https version of the request. But if whatever you're using to send the request doesn't correctly handle 302's it might resend the request incorrectly.

Resources