0

我正在编写一个需要与后端服务器通信的 iPad 应用程序。使用这个后端的首要任务是登录,为此服务器有一个我们可以 POST 到的 URL,我这样做是这样的:

    // Create the request.
NSString* loginURL = @"http://foo.local/signature/service/auth/rest/firewall/login";
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:loginURL]];

NSString* credentials = @"{\"userName\":\"foo2@foolinator.com\", \"password\":\"password\"}";

[request setHTTPMethod:@"POST"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:[credentials dataUsingEncoding:NSASCIIStringEncoding
                               allowLossyConversion:YES]];

// Logging in...
NSError* error = nil;
NSURLResponse* response;
NSData* result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
NSString* responseString = [NSHTTPURLResponse localizedStringForStatusCode:[httpResponse statusCode]];

NSLog(@"Response String is: %@\n", responseString);
NSLog(@"Header fields are: %@\n", [httpResponse allHeaderFields]);

奇怪的是,我得到的响应是错误 405:不允许方法。如果我正在执行 GET,我会预料到这一点,但我正在执行 POST。

我安装了 WireShark 来检查 HTTP 请求,似乎实际上有两个请求。第一个是 POST 调用,服务器返回一些 cookie 信息作为响应,然后是第二个 GET 调用,这就是上面的代码返回的内容。

为什么会这样?是否与服务器第一次的响应有关?

4

1 回答 1

1

在研究 Web 服务的登录 API 时,有一些不相关的观察结果:

  1. 如果从主队列执行此操作,您应该异步发送。永远不要从主队列发出同步网络请求。如果您在主队列上同步执行此操作 (a),您可能会冒着让 iOS 看门狗进程杀死您的应用程序的风险,如果在处理某些同步网络请求时主队列变得无响应,就会发生这种情况;(b) 在网络请求期间简单地冻结应用程序是一个糟糕的用户体验......如果需要,请禁用 UI 并UIActivityIndicatorView在网络请求正在进行时显示不确定的进度指示器 (a)。

  2. 您可能应该forHTTPHeaderFieldContent-Length. 这可能不是必需的,但这是一种很好的做法。

  3. 您可能不应该使用带有用户 ID 和密码的 JSON 字符串,而是应该NSDictionary使用类似NSJSONSerialization. 实际上,例如,如果您的密码包含任何需要转义的字符(例如引号),则现有代码可能不起作用。使用NSJSONSerialization是一种确保您的 JSON 格式正确的简单方法。

  4. 您可能不应该在 JSON 请求中以明文形式发送密码。至少,我希望你的服务器使用 HTTPS。

无论如何,通过这些观察,假设您的服务器确实期待 JSON 请求,我可能会建议类似:

// hopefully your production server is employing HTTPS

NSString *loginURL = @"https://foo.local/signature/service/auth/rest/firewall/login";

// use NSJSONSerialization to create JSON rather than building it in a NSString

NSDictionary *postDictionary = @{@"userName": userName, @"password": password}; // assuming you have NSString variables, `userName` and `password`
NSError *error = nil;
NSData *postData = [NSJSONSerialization dataWithJSONObject:postDictionary options:0 error:&error];
NSAssert(postData, @"dataWithJSONObject failed: %@", error);

// when creating request, also set Content-Length

NSURL *url = [NSURL URLWithString:loginURL];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:postData];
[request setValue:@"application/json; charset=utf-8" forHTTPHeaderField:@"Content-Type"];

// issue request asynchronously

[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    if (connectionError) {
        NSLog(@"sendAsynchronousRequest error: %@", connectionError);
        return;
    }

    // process the server response here
}];

您可能仍想使用基于NSURLConnectionDataDelegate/NSURLConnectionDelegate的请求(您可以识别重定向、质询、在需要时取消它等),但以上内容可能是基于 JSON 的异步请求的良好开端。

于 2013-10-29T20:26:46.917 回答