iOS:来自一个电话的两个请求?

时间:2013-10-29 18:36:40

标签: ios objective-c rest

我正在编写一个需要与后端服务器通信的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调用,这就是上面代码返回的内容。

为什么会这样?这是第一次来自服务器的响应吗?

1 个答案:

答案 0 :(得分:1)

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

  1. 如果从主队列执行此操作,则应该异步发送此信息。切勿从主队列发出同步网络请求。如果您在主队列(a)上同步执行此操作,则可能会让iOS监视程序进程终止您的应用程序,如果主队列在处理某些同步网络请求时无响应,则会发生这种情况; (b)在网络请求期间简单地冻结应用程序是一个糟糕的用户体验...如果需要,请在网络请求正在进行时禁用UI并显示不确定的进度指示器(UIActivityIndicatorView)。 / p>

  2. 您应该为forHTTPHeaderField设置值Content-Length。这可能不是必需的,但这是一种很好的做法。

  3. 您可能不应该使用带有用户ID和密码的JSON字符串,而是应该使用NSJSONSerialization之类的内容从NSDictionary构建此字符串。实际上,如果您的密码有任何需要转义的字符(例如引号),则现有代码可能无效。使用NSJSONSerialization是确保正确格式化JSON的简便方法。

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

  5. 无论如何,根据这些观察结果,假设您的服务器确实期待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的异步请求。