nimrod
nimrod

Reputation: 5742

NSMutableURLRequest with parameter - cannot parse response

I am trying to create a connection to a URL that prints out JSON code that is then parsed and processed. My URL has a parameter called bypass. The URL looks like this:

http://www.getchanged.net/json.asp?bypass=MIGCBgkrBgEEAYI3WAOxxx

I did not manage to solve this issue as it always says Connection failed with error: cannot parse response

NSURL *aUrl = [NSURL URLWithString:@"http://www.getchanged.net/json.asp"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];

[request setHTTPMethod:@"GET"];
NSString *postString = @"bypass=MIGCBgkrBgEEAYI3WAOxxx";
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];

Error:

2014-10-29 21:43:39.529 TestMap[57987:1931535] Connection failed with error: cannot parse response

What is wrong here?

Please note: I replaced the bypass hash a bit because it contains sensible data. The website works and is printing out JSON output.

Upvotes: 3

Views: 5762

Answers (5)

Yuvrajsinh
Yuvrajsinh

Reputation: 4584

The following works fine:

NSURL *aUrl = [NSURL URLWithString:@"http://www.getchanged.net/json.asp?bypass=MIGCBgkrBgEEAYI3WAOxxx"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
    [request setHTTPMethod:@"GET"];

    NSURLResponse *response = nil;
    NSError *error = nil;
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    if (!error) {
        NSLog(@"Response String : %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);

        id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
        NSLog(@"Parsed Data : %@", json);

    }

But it may happen that - as you mentioned in your Note - bypass hash is changed to hide your sensible data, that data may having some special characters or invalid JSON that is failing your JSON parsing. If you don't wan't to share that data you can edit the original response with dummy data (do not replace special characters), and share here that will help all to debug more.

Upvotes: 0

Craig
Craig

Reputation: 9330

Apart from the issue well addressed by Rob, the JSON return from the listed website doesn't validate:

I've tried several validators with the URL and bypass hash (you posted in comments) and the JSON is invalid. Typical error returned:

JSON Content: Invalid JSON at line 26, column 84
  Parse error on line 26:
  ...rtragen", "adresse":"fairtragen - GmbH 
  -----------------------^
  Expecting 'STRING', 'NUMBER', 'NULL', 'TRUE', 'FALSE', '{', '[', got 'undefined'

As explained at json.org any string value can contain

any UNICODE character except " or \ or control character

So a string value has as structure like this:

enter image description here

Note the escape format required for newlines and carriage returns.

Taking that into account, and looking at the raw output captured in Charles you can see that line 26, column 84 contains x0d followed by x0a (i.e. an old windows style CRLF) in the middle of a value.

The server should be escaping these values before sending them out.

By the way, the JSON has lots of spaces (x20) and CRLF through out, outside of the array, objects and values, that while not affecting the JSON, are unnecessary and do bulk up the data transfer.

Upvotes: 1

Rob
Rob

Reputation: 438257

A couple of observations:

  1. The key issue is that you are issuing a GET request, but supplying the bypass parameter in the body of the request. For POST requests, the parameters should be added to the body, but for GET requests, it should be added to the URL.

  2. If this bypass key could ever contain reserved characters (such as spaces, &, +, etc.), you'd want to percent-escape the value you're supplying for bypass. If you are guaranteed that it will be only alphanumeric, you can get away without percent escaping it, but as a general rule, you should always percent-escape HTTP parameters.

  3. While point #1 will probably resolve this issue, sometimes web services will expect certain header fields to be set. For example, it is common to set Content-Type header of the request (e.g., this looks like the Content-Type of the request should be application/x-www-form-urlencoded).

  4. You say that you are receiving an error message, "Connection failed with error: cannot parse response". You haven't shown us the code that is producing this error, but I presume this was generated when your code tried to parse a response as JSON when it wasn't.

    In the future, when diagnosing these issues, I find it useful to actually examine the details of the response. Notably, the header of the response will be contained in the NSURLResponse (which will actually be a NSHTTPURLResponse which has a statusCode numeric value which can be very illuminating). The body of the response will often be a string representation of the error (often when a web service fails it generates text or HTML description of the error).

    Whenever you get errors like this in the future, always look at the statusCode and the text of whatever the server returned, as that would help you diagnose what's going on. It will be more informative than a generic "I cannot parse the response" message.

  5. If you have a web interface that works, and an application interface that doesn't, it's sometimes useful to use a tool like Charles to observe the request generated by the web browser and compare that to the request generated by the app. This can be invaluable when diagnosing these sorts of issues.

    Also, if you use something like Charles, this allows you to manually inspect the status code and text response of the server which I suggested you might handle programmatically in point #4.

Upvotes: 4

mattr
mattr

Reputation: 5518

I think this is what you want:

NSString * bypassId = @"MIGCBgkrBgEEAYI3WAOxxx";
NSURL *aUrl = [NSURL URLWithString:[NSString stringWithFormat:@"http://www.getchanged.net/json.asp?bypass=%@",bypassId]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    NSString *jsonAsString = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
    NSLog(@"string: %@",jsonAsString);
    NSDictionary *jsonAsObject = [NSJSONSerialization JSONObjectWithData:data options: NSJSONReadingMutableContainers error: nil];
    NSLog(@"dict: %@",jsonAsObject);
}] resume];

This prints out (in the console):

2014-11-06 17:09:21.659 ] string: 
{"stores":[

]}

2014-11-06 17:09:21.659 ] dict: {
    stores =     (
    );
}

I have it printing the data as a json string and dictionary because I'm not exactly sure what you want.

Upvotes: 2

gabbler
gabbler

Reputation: 13766

Change the request HTTPMethod to POST, you will be fine.

@property (copy) NSData *HTTPBody;

@discussion This data is sent as the message body of the request, as
in done in an HTTP POST request.

You can also use GET in this way, set the whole url as the request url and don't set the HTTPBody.

NSString *urlAsString = @"http://www.getchanged.net/json.asp";
urlAsString = [urlAsString stringByAppendingString:@"?bypass=MIGCBgkrBgEEAYI3WAOxxx"];
NSURL *aUrl = [NSURL URLWithString:urlAsString];

This site confirmed this is a valid JSON string, however there is some "\r\n" in it, this code seems to be able to fix the problem by removing the "\r\n" from the string.

NSString* receivedString = [[NSString alloc] initWithData:receivedData encoding:NSASCIIStringEncoding];
receivedString = [receivedString stringByReplacingOccurrencesOfString:@"\r\n" withString:@""];

NSData *data = [receivedString dataUsingEncoding:NSUTF8StringEncoding];
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

Upvotes: 2

Related Questions