Sorry for the newb question, but I am tearing my hair out over this one. I can successfully POST data to my endpoint URL via a program like FireFox's POSTER. However, I'm trying to post that same JSON data from my app to my endpoint URL (Drupal services push_notifications end point), and for some reason it will not POST successfully. Here is the code that I'm using:
ViewController.m
NSString *urlString1 = @"http://app.com/endpoint001/push_notifications";
NSDictionary *jsonBodyDict = @{@"token":postDeviceID, @"type":@"ios"};
NSData *jsonBodyData = [NSJSONSerialization dataWithJSONObject:jsonBodyDict options:kNilOptions error:nil];
// watch out: error is nil here, but you never do that in production code. Do proper checks!
NSString *urlString2 = [NSString stringWithFormat:@"http://app.com/endpoint001/push_notifications?token=%@&type=%@",
postDeviceID,@"ios"];
NSMutableURLRequest *request = [NSMutableURLRequest new];
request.HTTPMethod = @"POST";
// for alternative 1:
[request setURL:[NSURL URLWithString:urlString1]];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[request setHTTPBody:jsonBodyData];
[request addValue:csrfToken forHTTPHeaderField:@"X-CSRF-Token"];
// for alternative 2:
[request setURL:[NSURL URLWithString:urlString2]];
// no body needed, though that ultimately depends on your server. Also, I didn't test this for lack of a backend :)
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config
delegate:nil
delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData * _Nullable data,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
NSLog(@"Yay, done! Check for errors in response!");
NSHTTPURLResponse *asHTTPResponse = (NSHTTPURLResponse *) response;
NSLog(@"The response is: %@", asHTTPResponse);
// set a breakpoint on the last NSLog and investigate the response in the debugger
// if you get data, you can inspect that, too. If it's JSON, do one of these:
NSDictionary *forJSONObject = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:nil];
// or
NSArray *forJSONArray = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:nil];
NSLog(@"One of these might exist - object: %@ \n array: %@", forJSONObject, forJSONArray);
}];
[task resume];
Note: I've put this code in AFTER my user already successfully logs in, so I'm not sure starting a whole new connection is necessary? How can I POST my data to the server if a session and CSRF Token already exists? What should my code look like? Whoever answers this question is going on my Christmas list... O_O
NSLog Response:
2017-07-17 17:31:11.421281-0700 app[977:206852] Yay, done! Check for errors in response!
2017-07-17 17:31:11.422198-0700 app[977:206852] The response is: <NSHTTPURLResponse: 0x170239b40> { URL: http://app.com/endpoint001/push_notifications?token=9526687d594944513b0wabf704eae3223f0de9bf69136a0aae3ab046863474b1&type=ios } { status code: 401, headers {
"Cache-Control" = "no-cache, must-revalidate";
Connection = "keep-alive";
"Content-Type" = "application/json";
Date = "Tue, 18 Jul 2017 00:14:35 GMT";
Expires = "Sun, 19 Nov 1978 05:00:00 GMT";
Server = Apache;
"Transfer-Encoding" = Identity;
Vary = Accept;
"X-Content-Type-Options" = nosniff;
} }
2017-07-17 17:31:27.172085-0700 app[977:207024] XPC connection interrupted
2017-07-17 17:31:27.172311-0700 app[977:206852] One of these might exist - object: (
"CSRF validation failed"
)
array: (
"CSRF validation failed"
)
tom's answer gives the right direction, but I see the lack of description what the code actually does is confusing to you. Also, you're using deprecated methods (of NSURLConnection
), so I've made a quick (untested) example using NSURLSession
and its related classes. Don't worry, it is basically the same.
That being said, the way you try this in your original code makes me wonder whether you're actually really sending a json body (i.e. your backend expects that) or rather the system relies on boring parameters to the URL. So I added both ways in my code:
NSString *postDeviceID = @"something";
NSString *urlString1 = @"http://myurl.com/endpoint01/push_notifications";
NSDictionary *jsonBodyDict = @{@"token":postDeviceID, @"type":@"ios"};
NSData *jsonBodyData = [NSJSONSerialization dataWithJSONObject:jsonBodyDict options:kNilOptions error:nil];
// watch out: error is nil here, but you never do that in production code. Do proper checks!
NSString *urlString2 = [NSString stringWithFormat:@"http://myurl.com/endpoint01/push_notifications?token=%@&type=%@",
postDeviceID,@"ios"];
NSMutableURLRequest *request = [NSMutableURLRequest new];
request.HTTPMethod = @"POST";
// for alternative 1:
[request setURL:[NSURL URLWithString:urlString1]];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[request setHTTPBody:jsonBodyData];
// for alternative 2:
[request setURL:[NSURL URLWithString:urlString2]];
// no body needed, though that ultimately depends on your server. Also, I didn't test this for lack of a backend :)
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config
delegate:nil
delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData * _Nullable data,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
NSLog(@"Yay, done! Check for errors in response!");
NSHTTPURLResponse *asHTTPResponse = (NSHTTPURLResponse *) response;
NSLog(@"The response is: %@", asHTTPResponse);
// set a breakpoint on the last NSLog and investigate the response in the debugger
// if you get data, you can inspect that, too. If it's JSON, do one of these:
NSDictionary *forJSONObject = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:nil];
// or
NSArray *forJSONArray = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:nil];
NSLog(@"One of these might exist - object: %@ \n array: %@", forJSONObject, forJSONArray);
}];
[task resume];
It is also possible your backend supports both ways, but learning how to construct a proper json body is always helpful.
Note that the reason why tom's code makes you think it posted is that you seem to misunderstand how NSURLConnection
works (unless you excluded code you rely on): The object is always constructed the way you're using it, so if (conn)
is always in the YES
branch. That doesn't mean the connection, i.e. the actual loading succeeds. The initializer returns nil
if it can't create an instance, not if a valid instance simply relies on data that is refused on connecting to its target. You don't get an error for that in this way (you need to rely on delegation for this).
The session approach I showed gives you a completion block in which you can investigate what the server responds to track down further what went wrong. You can just set a breakpoint there and look at the error (which is nil
if it succeeds), the status in the response etc.
I obviously didn't run this exact code, so excuse any typos, but I have used this approach a lot with loading from and sending to a variety of backends for my usual work. In general it should work (and, more importantly, allow you to figure out what the heck your backend expects, assuming you don't have detailed docs about it).
Edit:
Okay, that hint in the code comment was maybe misleading: I meant to check the response for errors. The response
object (also see the documentation for the dataTask...
method in general). I added code helping a bit with that, but please be aware that the conversion of the data
object into JSON depends on what and if your server actually passes any data at all. Or it might have a weird format or something, you need to figure this out. For simple stuff that gets POSTed, it might not provide any data, you simply get an according information about whether it worked or not in the response
. Note especially NSHTTPURLResponse
's statusCode
property. Unless your server does something weird, that's the HTTP status code defined by the standard and should help you out. If, for example, the way in which you constructed your body in the request (the jsonBodyDict
in my example code) was wrong (wrong element names or the like) you would get a 400.
In general you have to understand that the meaning of "error" is not that simple here. The methods consider not being able to connect at all as an error, so you get an error object in the completion block in these cases. This is typically the case when your server simply doesn't exist or the like. If, however, you simply fail to communicate your intended information to your server, that's not an error from the API's perspective and you don't get an error object. You get a valid response that says "this request was erroneous". You're doing good already in your case, as the connection seems to happen and work, you're just not yet sending the data your server expects. The response
should give more info on that.
To further debug this would go beyond the scope of this question even more, because ultimately we're talking about how your specific backend behaves now. If you can't figure that out, you'd need to give me the credentials for your server so I can test it myself, everything else would be guessing on my part now. :) We can do that, but it's probably best to do so in chat and not in a question/answer that's already that long, hehe.
All the Answers are good but i am posting a method , for i proper implementation of posting JSON
data to POST API
POST Method
-(void)postServiceAPI:(NSString*)url andParameters:(NSString *)parameterString andCompletionHandler:(void(^)(NSData *data,NSError *error,BOOL networkError))compilationHandler
{
// Checking the Network availability : You can skip this check
Reachability *netConnectionReach = [Reachability reachabilityWithHostname:@"www.google.com"];
NetworkStatus internetStatus = [netConnectionReach currentReachabilityStatus];
if ((internetStatus != ReachableViaWiFi) && (internetStatus != ReachableViaWWAN))
{
dispatch_async(dispatch_get_main_queue(), ^{
compilationHandler(nil,nil,TRUE);
});
}
else
{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
configuration.timeoutIntervalForRequest = 30.0;
configuration.timeoutIntervalForResource = 60.0;
configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalAndRemoteCacheData;
_session = [NSURLSession sessionWithConfiguration:configuration];
NSURL *urlStr = [NSURL URLWithString:url];
NSData *postData = [parameterString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:@"%lu",(unsigned long)[postData length]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:urlStr
cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
// You can also set " application/json"
// This will solve your problem
[ request setValue:@"Your_Token" forHTTPHeaderField:@"your_key_for_token"];
[request setHTTPBody:postData];
[[_session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if (error)
{
dispatch_async(dispatch_get_main_queue(), ^{
compilationHandler(nil,error,nil);
});
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
compilationHandler(data,nil,nil);
});
}
}]resume];
}
}
Uses:
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
[dict setObject:postDeviceID forKey:@"token"];
[dict setObject:@"iOS" forKey:@"type"];
NSData * JsonData =[NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:nil];
NSString *urlStr = @"http://myurl.com/endpoint01/push_notifications"
NSString *parameterJsonString= [[NSString alloc] initWithData:JsonData encoding:NSUTF8StringEncoding];
// If you Don't want to create Dictionary than just send your Parameter JSon what you have made , please make sure if json is correct or not
//parameterJsonString = [NSString stringWithFormat:@"token=%@&type=%@",postDeviceID,@"ios"];
NSLog(@"URL:%@ Request :\n\n %@",urlStr,parameterJsonString);
[self postServiceAPI:urlStr andParameters:parameterJsonString andCompletionHandler:^(NSData *data, NSError *error, BOOL networkError) {
// If Data Retrun By API is not nil
if (data)
{
// Here is your Response
NSDictionary *dicData=[NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
}
else
{
// Show Alert When data is Not Recived from API
if (networkError)
{
// No Internet Connection
}
else
{
// Network Error
NSLog(@"Network Error : %@",error.description);
}
}
}];
i hope this will help you
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With