Obj-C – 将JSON数据POST到服务器?

对不起,这个新问题,但我正在撕裂我的头发。 我可以通过像FireFox的POSTER这样的程序成功地将数据发布到我的端点URL。 但是,我正在尝试将相同的JSON数据从我的应用程序发布到我的端点URL(Drupal services push_notifications终点),并且由于某种原因它不会成功POST。 这是我正在使用的代码:

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]; 

注意:我已将此代码放入用户已成功登录后,因此我不确定是否需要启动全新连接? 如果会话和CSRF令牌已存在,如何将数据发布到服务器? 我的代码应该是什么样的? 回答这个问题的人正在我的圣诞节清单上… O_O

NSLog响应:

 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:  { 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" ) 

汤姆的答案给出了正确的方向,但我发现缺乏描述代码实际上做的事情让你感到困惑。 此外,您正在使用弃用的方法( NSURLConnection ),因此我使用NSURLSession及其相关类NSURLSession了一个快速(未经测试)的示例。 别担心,它基本相同。

话虽这么说,你在原始代码中尝试这种方式的方式让我想知道你是否真的真的发送了一个json体(即你的后端期望)或者更确切地说系统依赖于无聊的URL参数。 所以我在我的代码中添加了两种方式:

 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]; 

你的后端也可以支持两种方式,但学习如何构建一个合适的json体总是有帮助的。 请注意,tom的代码让你认为它发布的原因是你似乎误解了NSURLConnection工作原理(除非你排除了你依赖的代码):对象总是以你使用它的方式构造,所以if (conn)是总是在YES分支。 这并不意味着连接,即实际加载成功。 如果初始化程序无法创建实例,则返回nil ,而不是有效实例仅依赖于连接到其目标时被拒绝的数据。 你不会以这种方式得到错误(你需要依赖委托)。

我展示的会话方法为您提供了一个完成块,您可以在其中调查服务器响应的内容,以进一步跟踪出错的地方。 你可以在那里设置断点并查看错误(如果成功则为nil ),响应中的状态等。

我显然没有运行这个确切的代码,所以原谅任何错别字,但我已经使用这种方法很多加载和发送到我的日常工作的各种后端。 一般情况下,它应该工作(更重要的是,让你弄清楚你的后端期望什么,假设你没有关于它的详细文档)。


编辑

好的,代码注释中的提示可能会产生误导:我打算检查错误的响应response对象(一般也参见dataTask...方法的文档)。 我添加了一些代码,但请注意,将data对象转换为JSON取决于您的服务器实际传递任何数据的内容。 或者它可能有一种奇怪的格式或其他东西,你需要弄清楚这一点。 对于获得POST的简单内容,它可能不提供任何数据,您只需获得有关response是否有效的相关信息。 特别注意NSHTTPURLResponsestatusCode属性。 除非您的服务器做了一些奇怪的事情,否则这是标准定义的HTTP状态代码 ,应该可以帮助您。 例如,如果您在请求中构造主体的方式(我的示例代码中的jsonBodyDict )是错误的(错误的元素名称等),那么您将获得400。

一般来说,你必须明白“错误”的含义并不是那么简单。 这些方法认为根本无法连接为错误,因此在这些情况下,您将在完成块中获得错误对象。 当您的服务器根本不存在或类似情况时,通常会出现这种情况。 但是,如果您只是无法将预期信息传达给服务器,那么从API的角度来看,这不是错误,并且您没有得到错误对象。 您收到的回复显示“此请求错误”。 在您的情况下,您已经做得很好,因为连接似乎正在发生并正常工作,您还没有发送服务器期望的数据。 response应该提供更多信息。

为了进一步调试,这将超出这个问题的范围,因为最终我们正在谈论你的特定后端如何表现。 如果你无法弄明白,你需要给我服务器的凭据,这样我就可以自己测试,其他一切都会在我现在猜测。 ,我们可以做到这一点,但最好是在聊天中这样做,而不是在已经很久的问题/答案中,呵呵。

所有的答案都很好,但我发布了一个方法,我正确实现了向POST API发布JSON数据

POST方法

  -(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]; } } 

用途:

  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); } } }]; 

我希望这能帮到您

 //Generate your postData NSDictionary *postDict = @{@"token":postDeviceID, @"type":@"ios"}; //convert object to data NSError *error; NSData *postData = [NSJSONSerialization dataWithJSONObject:postDict options:kNilOptions error:&error]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; [request setHTTPMethod:@"POST"]; [request setURL:[NSURL URLWithString:@"http://myurl.com/endpoint01/push_notifications"]]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; [request setHTTPBody:postData]; NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self]; if(conn) { NSLog(@"Connection Successful"); } else { NSLog(@"Connection could not be made"); } 

您的JSON数据是否必须采用字符串或字典的forms,如汤姆所述

NSString *post = [NSString stringWithFormat:@"token=%@&type=%@",postDeviceID,@"ios"]; – 这是对的吗?

如是。

您可以使用NSURLConnection而不是委托方法来检查异步请求。

 NSString *post = [NSString stringWithFormat:@"token=%@&type=%@",postDeviceID,@"ios"]; NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; NSString *postLength = [NSString stringWithFormat:@"%lu",(unsigned long)[postData length]]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; [request setHTTPMethod:@"POST"]; [request setValue:postLength forHTTPHeaderField:@"Content-Length"]; [request setURL:[NSURL URLWithString:@"http://myurl.com/endpoint01/push_notifications"]]; [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; [request setHTTPBody:postData]; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { if(data!=nil) { id res =[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]; } }]; 

希望能帮助到你。