How to send HTTP body plain text for GET method? - ios

I had a problem, I use the iGDB REST API which need to send some plain text for some endpoints with GET method.
There is no problem with PostMan (by selecting "Body" > "raw" & paste my query), but when I try with Objective-C, an error appear telling me "GET method must not have a body"...
Here is the code used in my app:
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"MY_URL"]];
[request addValue:#"text/plain" forHTTPHeaderField:#"Content-Type"];
[request setHTTPMethod:#"GET"];
[request setHTTPBody:[#"sort popularity desc;" dataUsingEncoding:NSUTF8StringEncoding]];
EDIT 02/10/2019
Trying to add each filters in headers not working...
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"MY_URL"]];
[request setValue:[[#"id,name,first_release_date,release_dates,cover,platforms" dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0] forHTTPHeaderField:#"fields"];
[request setValue:[[#"popularity desc" dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0] forHTTPHeaderField:#"sort"];
[request setValue:[[#"5" dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0] forHTTPHeaderField:#"limit"];
[request setValue:[[#"0" dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0] forHTTPHeaderField:#"offset"];
Thanks in advance for any replies !

It's no longer possible to send a GET request with a body, you'll have to send the data via the query string of the URL, either by building the string manually or with the help of NS(Mutable)URLComponents.
As per the iOS 13 release notes, GET requests are no longer allowed to have a body:
All URLSessionTask instances with a GET HTTP method that contain a body now produce the error NSURLErrorDataLengthExceedsMaximum. (46025234)
This makes URLSession more conformant with the HTTP/1.1 RFC:
A message-body MUST NOT be included in
a request if the specification of the request method (section 5.1.1)
does not allow sending an entity-body in requests.

If I get true your question, you can set parameters to your request header when you get you can set like below.
[request addValue:#"valueForKey1" forHTTPHeaderField:#"key1"];
[request addValue:#"valueForKey2" forHTTPHeaderField:#"key2"];
If you send a data format so,
NSString *stringValueOfParameters =[NSString stringWithFormat:#"userName:blabla"];
NSData *convertedDat=[stringValueOfParameters dataUsingEncoding:NSUTF8StringEncoding];
NSString *headerValue=[NSString stringWithFormat:#"Basic %#",[convertedDat base64EncodedStringWithOptions:0]];
[request setValue:headerValue forHTTPHeaderField:#"headerKey"];
// Edit
You must try kinda like that, directly write a Dictionary or like below write your JSON parameters as NSString then convert it to Data.
NSDictionary *dictParams = #{ #"fields" : #[#"id",#"name",#"first_release_date",#"release_dates",#"cover",#"platforms"], #"sort" : #"popularity desc", #"limit": #5, #"offset":#0 };
// this is taken from an example
NSString *jsonString = #"{\"ID\":{\"Content\":268,\"type\":\"text\"},\"ContractTemplateID\":{\"Content\":65,\"type\":\"text\"}}";
NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
Then try it to set header.

Related

Using NSURLSession to POST, what is the correct way to post the variables?

I am following this tutorial: http://www.raywenderlich.com/2965/how-to-write-an-ios-app-that-uses-a-web-service. Trying to set up a basic web service. Seems like the tutorial is old material and ASIHTTPRequest is no longer continued. I have been trying to use NSURLRequest instead. First question, is NSURLRequest a pretty standard way to be doing this? I just want something for basic GET, POST etc, should I be doing it a different way?
My code is:
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
NSLog(#"We want to unlock for the code %#",self.textField.text);
//Get a device ID, (actually can't do this aymore)
NSString *uniqueIdentifier = #"My iPhone";
NSString *code = self.textField.text;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://www.madasd.co/promos/"]];
request.HTTPMethod=#"POST";
//Set the header fields
[request setValue:#"application/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
NSString *myString = [NSString stringWithFormat:#"rw_app_id=1&code=%#&device_id=%#",code,uniqueIdentifier];
NSLog(#"%#",myString);
NSData *requestBodyData = [myString dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPBody=requestBodyData;
//Create url and fire request
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[conn start];
return TRUE;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#",string);
}
Second question, I have tested the backend using curl so I know it works fine, however the response I get is "Invalid Request", I think this is because the string I am sending is not correct. Am I doing this correct using the var names and & operators? Any pointers on this would be great! thanks. (Running a LAMP server on Linode!)
EDIT:
Also tried sending as JSON:
[request addValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request addValue:#"application/json" forHTTPHeaderField:#"Accept"];
NSDictionary *mapData = [[NSDictionary alloc]initWithObjectsAndKeys:#"1",#"rw_app_id",code,#"code",uniqueIdentifier,#"device_id", nil];
NSError *error = nil;
NSData *requestBodyData = [NSJSONSerialization dataWithJSONObject:mapData options:0 error:&error];
request.HTTPBody=requestBodyData;
Still getting the same error.
A couple of thoughts:
Don't use NSURLConnection. It is deprecated as of iOS 9. Use NSURLSession. See Using NSURLSession in the URL Loading System Programming Guide.
Decide what type of request you need to prepare. You specified application/xml in your header, but are creating a application/x-www-form-urlencoded request. Your Content-Type header must match how you're building the HTTPBody.
What type of request does your server require? x-www-form-urlencoded? XML? JSON?
Also, what type of response does your server provide?
If building a application/x-www-form-urlencoded request (as suggested by the body of your request), you are not properly percent escaping the values (see https://stackoverflow.com/a/20398755/1271826).
If you use delegate based NSURLConnection or NSURLSession, you should not just grab the results in didReceiveData. What you need to do is
Instantiate a NSMutableData before starting the request;
Have didReceiveData merely append to that NSMutableData;
Only when connectionDidFinishLoading: (in NSURLConnection) or URLSession:task:didCompleteWithError: (in NSURLSession) is called, should you then use the NSMutableData.
Alternatively, if using the block-based NSURLSession, this concern is completely eliminated (since you're not implementing any delegate methods). Using completionHandler-based methods of NSURLSession is much easier.
If all of this is too complicated, you might consider using AFNetworking's AFHTTPSessionManager (but not AFHTTPRequestOperationManager) to build your requests. It gets you out of the weeds of properly building requests, implementing delegate methods, etc.
You might need to wrap the strings into a dictionary and get the NSData object from a call to NSJSONSerialization. Though it depends on the form expected by the server.

NSURLRequest HTTPBody is null

I've been trying to send POST requests to a server (which I know works from using Postman) using NSURLSession.
I create a request as follows:
NSMutableURLRequest *request = [self createRequestWithPath:[self getPathForEndpoint:endpoint]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:params];
params is created from a serialised parameter string (which I know is a legal parameter string), as follows:
NSData *params = [[self serialiseParameters:data] dataUsingEncoding:NSUTF8StringEncoding];
(For reference, the serialised parameter string is something like #"title=MyTitle&somethingElse=0".
From searching around SO and other places, this seems like this is the correct way to set up the request such that NSURLSession can POST the data, but the server is showing nothing coming through in the HTTP Body.
Is there another way to POST data?
This was fixed by not using the setters setHTTPBody at all, and using NSJSONSerialization instead. The working code looks like this:
// Encode parameters
NSData *postData = [NSJSONSerialization dataWithJSONObject:data options:0 error:nil];
// Construct request
NSMutableURLRequest *request = [self createRequestWithPath:[self getPathForEndpoint:endpoint]];
request.HTTPMethod = #"POST";
request.HTTPBody = postData;
I had tried using this before, but for some reason it didn't work properly when I used [request setHTTPBody], setting the property directly solved this.

How to post data in the form of url in iOS?

I want to send my UITextfields data to a server.
I want to post data but the server showing error message to me.
Please check my code:
...
NSURL *url=[NSURL URLWithString:#"http://projectsatseoxperts.net.au/fishing/api/postRegister.php"];
NSString *post =[[NSString alloc] initWithFormat:#"FirstName=%#&LastName=%#userName=%#&Email=%#Phone=%#&Address=%#Password=%#&ConfirmPassword=%#",
txt_firstname.text,txt_lastname.text,txt_username.text,txt_email.text,txt_phone.text,txt_address.text,txt_password.text,txt_confirmpassword.text];
NSLog(#"Post is: %#",post);
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSLog(#"postData is: %#",postData);
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSLog(#"postLength is: %#",postLength);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
//[request setValue:#"http://projectsatseoxperts.net.au/fishing/api/postRegister.php" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSLog(#"request is: %#", [request allHTTPHeaderFields]);
NSError *error;
NSURLResponse *response;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"urlData is: %#",urlData);
NSString *data=[[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
NSLog(#"%#",data);
}
After post the details the values will come here - http://projectsatseoxperts.net.au/fishing/api/register.php
Any idea or suggestions would be highly welcome.
A couple of observations:
The way you phrase your question, you would seem to be suggesting that you're trying to create a application/x-www-form-urlencoded request. If so, you should specify your Content-Type accordingly:
[request setValue:#"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
If doing a application/x-www-form-urlencoded request, then you must percent escape the data that you post, using CFURLCreateStringByAddingPercentEscapes (note, not stringByAddingPercentEscapesUsingEncoding). If any of your fields included any reserved characters, your query would fail.
- (NSString *)percentEscapeURLParameter:(NSString *)string
{
return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)string,
NULL,
(CFStringRef)#":/?#!$&'()*+,;=",
kCFStringEncodingUTF8));
}
Also, your post string is missing a few ampersands. I might solve this problem by using a dictionary:
NSDictionary *dictionary = #{#"FirstName" : txt_firstname.text,
#"LastName" : txt_lastname.text,
#"userName" : txt_username.text,
#"Email" : txt_email.text,
#"Phone" : txt_phone.text,
#"Address" : txt_address.text,
#"Password" : txt_password.text,
#"ConfirmPassword" : txt_confirmpassword.text};
And then build the post variable, invoking percentEscapeURLParameter for each value, like so:
NSMutableArray *postArray = [NSMutableArray array];
[dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
[postArray addObject:[NSString stringWithFormat:#"%#=%#", key, [self percentEscapeURLParameter:obj]]];
}];
NSString *post = [postArray componentsJoinedByString:#"&"];
By the way, those field names look suspect (with lowercase "u" in "userName"; often they're all lowercase field names). Are you sure about those field names?
Beyond that, you have to show us the error that you're getting.
Your "post" strings format look to be incorrect. You are missing some "&" in between certain fields i.e between "LastName" and "userName". Check your string as the server may not recognise the values in the string. Unless there is a specific reason for this.
Your Request is proper.. just check the content Type and Encoding of your post data.. Also do consult with the server team for exact data format they expect from you..
I suspect that since the API is returning XML, it is expecting XML in the HTTP POST. Contact the API developer to find out what data formats the API supports and the schema it expects.
There a number of issues with your code:
Potentially incorrect character encoding:
In your code:
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
you allow a lossy conversion from the character encoding of the original string to the one used for your post data. This will lead to incorrect parameters when they contain non-ASCII characters.
No Content-Type header set
Since you didn't set a content type, your data will be treated by the server as an unstructured text of ASCII characters, e.g. Content-type: text/plain; charset=us-ascii.
You probably want to use application/x-www-form-urlencoded, or better application/json - if the server accepts that MIME type.
When you set a Content-Type you also need to ensure your parameters are properly encoded according the Content-Type you set.
In order to solve these issues, I would suggest to try application/json as Content-Type and encode your parameters as JSON. Using Content-type: application/x-www-form-urlencoded is also possible, but this requires a much more elaborated encoding algorithm. Just try JSON:
So, instead of having this ugly string
NSString *post =[[NSString alloc] initWithFormat:#"FirstName=%#&LastName=%#userName=%#&Email=%#Phone=%#&Address=%#Password=%#&ConfirmPassword=%#",
txt_firstname.text,txt_lastname.text,txt_username.text,txt_email.text,txt_phone.text,txt_address.text,txt_password.text,txt_confirmpassword.text];
create a corresponding NSDictionary object. Then serialize it to a NSData object containing the JSON using NSJSONSerialization. Use this data object for your body, and set Content-Type: application/json.

Fetching an access token for youtube api iOS "Error" : "invalid_request"

I'm trying to get an access token for a youtube app for iOS. Here's the relevant code I have been using from my viewDidLoad method:
mAuth = [[GTMOAuth2Authentication alloc]init];
[mAuth setClientID:#"<MY CLIENT ID>"];
[mAuth setClientSecret:#"<MY CLIENT SECRET>"];
[mAuth setRedirectURI:#"urn:ietf:wg:oauth:2.0:oob"];
[mAuth setScope:#"https://gdata.youtube.com"];
[self.web loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://accounts.google.com/o/oauth2/auth?client_id=%#&redirect_uri=%#&scope=%#&response_type=code&access_type=offline", mAuth.clientID, mAuth.redirectURI, mAuth.scope]]]];
After this is called, the user has to grant access to their account, then I retrieve the auth code from the resulting page in the following code:
NSString *string = [self.web stringByEvaluatingJavaScriptFromString:#"document.title"];
if([string rangeOfString:#"Success"].location != NSNotFound){
NSLog(#"This is the code page");
NSString *importantCode = [[string componentsSeparatedByString:#"="] objectAtIndex:1];
NSLog(#"%#", importantCode);
if([self.defaults objectForKey:#"super important code"] == nil){
[self.defaults setObject:importantCode forKey:#"super important code"];
[self.defaults synchronize];
NSString *post = [NSString stringWithFormat:#"code=%#&client_id=%#&client_secret=%#&redirect_uri=%#&grant_type=code", [self.defaults objectForKey:#"super important code"], mAuth.clientID, mAuth.clientSecret, mAuth.redirectURI];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]init];
[request setURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://accounts.google.com/o/oauth2/token"]]];
[request setHTTPMethod:#"POST"]; [request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
[self.web loadRequest:request];
}
[timer invalidate];
}
After that, I should be given the access token in response to my POST request, but instead I get a page that simply says:
{
"error" : "invalid_request"
}
Does anyone (see where I went wrong)/(know how to retrieve the access token)?
I’d be interested in knowing what the HTTP status code is... I suspect ”invalid_request” means 400, which almost always means there’s some irritating stupid little error in the way you composed your authent URI. I don’t know enough iOS/ObcJ to tell if you’ve properly URLescaped any funny characters in any of the parameter values, but it’s worth checking. Or a typo in one of the values, or an extra newline creeping in or something?
It was as simple as this, I had set the grant_type parameter to 'code' when it should have been 'authorisation_code' if you are reading this and you are using the youtube api, don't make the same mistake. If you're reading this and you're sending a POST request in general, this error "invalid_request" means that either you skipped one of the parameters you should have added, or you added it incorrectly.

HTTP Status Code 411 - Length Required

I try to get data from server. I use NSURLConnectionDelegate, NSURLConnectionDataDelegate. There is code (Objective - C).
-(void)sendRequest
{
NSURL* url = [[NSURL alloc] initWithString:#"http://SomeServer"];
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] init];
NSString* reqBody = [NSString stringWithFormat:#"<tag>Content</tag>"];
NSData* reqData = [reqBody dataUsingEncoding:NSUTF8StringEncoding];
NSInputStream* stream = [NSInputStream inputStreamWithData:reqData];
[request setURL:url];
[request setHTTPBodyStream:stream];
[request setHTTPMethod:#"POST"];
self.wpData = [[NSMutableData alloc] init];
NSURLConnection* conection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[conection start];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self.wpData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)d {
NSString* str = [[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding];
NSLog(#"RESPONSE DATA: %#",str);
[self.wpData appendData:d];
}
But I get "411 - Length Required" when I use
[request setHTTPBodyStream:stream];
and "HASH (someAddress)" when I use
[request setHTTPBody:reqData];
I tried
[request setHTTPBodyStream:stream];
NSString *postLength = [NSString stringWithFormat:#"%d", [reqData length]];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
but again "HASH (someAdddress)"
What have I done wrong?
Sorry for my English. Thanks :)
What have I done wrong?
Nothing.
HTTP Status Code 411 (Length Required) is sent by the server as a response when it refuses to accept a message without a content-length header, for whatever reason.
A server simply may or may not accept a content without a Content-Length header.
When you set an NSInputStream object as request body via property HTTPBodyStream for the request, NSURLConnection cannot evaluate the length of the body itself anymore. (there is no property length for a stream). Hence, NSURLConnection uses a certain "transfer mode", namely "chunked transfer encoding". This transfer mode should succeed to transmit any body and it does not require a Content-Legth header (actually must not contain one). Alas, the server simply does not accept this type of transfer.
See also: Chunked transfer encoding (wiki).
To solve the issue on the client side:
Determine the length of the body yourself (if possible) and set a "Content-Length" header field for the request. If this input stream has been created from a file or from a NSData object, the length can be easily determined. But be sure to set the exact same length as the actual stream content in bytes.
Don't use a NSInputStream, but use a NSData object as body and set it via property HTTPBody. When you set the body as a NSData object, NSURLConnection can determine the content length itself, and it will automatically add a Content-Length header with the correct length, unless you set it yourself in the request.

Resources