Obj-C - 将JSON数据POST到服务器? [英] Obj-C - POST JSON data to server?

查看:100
本文介绍了Obj-C - 将JSON数据POST到服务器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

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回复:

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


推荐答案

汤姆的回答给出了正确的方向,但我看到缺乏描述代码实际上做的是让你感到困惑。此外,您正在使用弃用的方法( NSURLConnection ),因此我使用 NSURLSession 及其相关课程。别担心,它基本上是一样的。

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.

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

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

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

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).

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

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).

编辑

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

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.

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

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.

这篇关于Obj-C - 将JSON数据POST到服务器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆