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.
Related
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.
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.
I'm trying to persist data from a microsoft web service call to a file when my app starts up. If the app is able to complete the webservice request, it's storing the data in an NSData object. Assuming the data has been successfully requested and stored, I want to execute certain code that I would NOT want to if the webservice is unsuccessful.
My webservice request is as follows:
NSMutableURLRequest *request1 = [[NSMutableURLRequest alloc]init];
[request1 setURL:[NSURL URLWithString:#"---URL---"]];
[request1 setHTTPMethod:#"GET"];
NSURLResponse *request1Response;
NSData *request1Handler = [NSURLConnection sendSynchronousRequest:request1 returningResponse:&request1Response error:nil];
NSString *request1Reply = [[NSString alloc]initWithBytes:[request1Handler bytes] length:[request1Handler length] encoding:NSASCIIStringEncoding];
NSData *data1 = [request1Reply dataUsingEncoding:NSUTF16StringEncoding];
So basically, the response is dropped into that data1 object.
When connected to the internet, the request executes fine. The code that follows is wrapped in a if(data1){ conditional to make sure that the webservice request is successful before executing it. The problem is that when I disconnect from the internet(and cut off access to that webservice), the code inside the conditional is still being executed.
I tried comparing data1 to nil, logging data1 to do a direct comparison of the contents, etc, but I gather that that data1 object isn't nil; it probably contains some sort of failure message that I have thus far been unable to access. What can I do in the conditional or in the webservice request itself to figure out when the request fails?
Create an NSError object and pass it as argument to the sendSynchronousRequest: method, then if there is network or another error, the err object will populated with error information hence it will not be nil. That means you can check if(!err)contiune else there is an error
check the code:
NSMutableURLRequest *request1 = [[NSMutableURLRequest alloc]init];
[request1 setURL:[NSURL URLWithString:#"http://www.google.com"]];
[request1 setHTTPMethod:#"GET"];
//object where error will be saved
NSError *err;
NSURLResponse *request1Response;
NSData *request1Handler = [NSURLConnection sendSynchronousRequest:request1 returningResponse:&request1Response error:&err];//pass the err here by reference
NSString *request1Reply = [[NSString alloc]initWithBytes:[request1Handler bytes] length:[request1Handler length] encoding:NSASCIIStringEncoding];
NSData *data1 = [request1Reply dataUsingEncoding:NSUTF16StringEncoding];
and then you can check here:
if (err){
NSLog(#"error: %#", err);
}
You should at least be checking for an error response by populating the error pointer parameter.
Ideally you should be using a different API which gives you access to more details such as the response HTTP status code which you should be using to determine what happened even if you did get something which looks like success.
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.
How to send an image as Multipart form object as a parameter to url from ios to spring mvc
url which is accepting at the back end in www.test.com?id=test&multipartforObject=
So in IOS how can i convert the image to Multipartform object to pass it as param along with the URL.
Manjush
In practice, many clients and servers do only support URLs up to a certain length.If you pass image as param along with the URL,sometimes, URL's length maybe to too long.
I suggest using HTTP/POST to upload multipart files.There is some sample code using ASIFormDataRequest to upload multipart files:
NSData * imageData1 = UIImageJPEGRepresentation([UIImage imageNamed:#"image1.png"] ,0.6);
NSData * imageData2 = UIImageJPEGRepresentation([UIImage imageNamed:#"image3.png"] ,0.6);
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request addData:imageData1 withFileName:#"image1.jpg" andContentType:#"image/jpg" forKey:#"certImage"];
[request addData:imageData2 withFileName:#"image2.jpg" andContentType:#"image/jpg" forKey:#"certImage"];
[request startAsynchronous];