NSURLConnection 返回错误而不是响应 401

Posted

技术标签:

【中文标题】NSURLConnection 返回错误而不是响应 401【英文标题】:NSURLConnection returning error instead of response for 401 【发布时间】:2015-04-01 13:17:30 【问题描述】:

我有一个 Web API,对于特定请求,如果一切正常,则返回状态代码 200,如果用户未基于授权令牌登录,则返回 401。如果响应状态为 200,则一切正常,但如果响应状态为 401,则似乎无法正常工作,返回代码为 -1012 的连接错误,而响应为 nil。

所以,下面的代码:

[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) 
    NSLog(@"%@", response);
    NSLog(@"%@", connectionError);

    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
    int statusCode = (int)[httpResponse statusCode];
    NSLog(@"response status code: %d", statusCode);

会显示

2015-04-01 15:58:18.511 MyProject[3618:694604] <NSHTTPURLResponse: 0x155facc0>  URL: *SOME_URL*   status code: 200, headers 
    "Access-Control-Allow-Headers" = "Content-Type, Accept, X-Requested-With";
    "Access-Control-Allow-Methods" = "POST, GET, PUT, UPDATE, OPTIONS";
    "Access-Control-Allow-Origin" = "*";
    Connection = "keep-alive";
    "Content-Type" = "application/json";
    Date = "Wed, 01 Apr 2015 12:58:14 GMT";
    Server = "Wildfly 8";
    "Transfer-Encoding" = Identity;
    "X-Powered-By" = "Undertow 1";
 
2015-04-01 15:58:18.513 MyProject[3618:694604] (null)
2015-04-01 15:58:18.513 MyProject[3618:694604] response status code: 200

如果响应状态是200,而如果状态码是401,我会得到:

2015-04-01 16:05:55.988 MyProject[3633:695836] (null)
2015-04-01 16:05:55.992 MyProject[3633:695836] Error Domain=NSURLErrorDomain Code=-1012 "The operation couldn’t be completed. (NSURLErrorDomain error -1012.)" UserInfo=0x146137c0 NSErrorFailingURLKey=*SOME_URL*, NSErrorFailingURLStringKey=*SOME_URL*, NSUnderlyingError=0x1459e6d0 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1012.)"
2015-04-01 16:05:55.992 MyProject[3633:695836] response status code: 0

如果我使用 Postman 或 android 设备执行相同的请求,我将获得带有以下标头的状态代码 401(从 Postman 复制):

Connection → keep-alive
Content-Length → 30
Content-Type → application/json
Date → Wed, 01 Apr 2015 13:07:34 GMT
Server → Wildfly 8
X-Powered-By → Undertow 1

是否有任何修复或库可以给我一些准确的响应状态?我搜索了一些关于 -1012 错误的信息,但找不到太多,我真的不想以此为基础。

编辑:经过一番研究,我在 Appl 的文档中找到了以下声明:“如果需要身份验证才能下载请求,则必须将所需的凭据指定为 URL 的一部分。如果身份验证失败,或缺少凭据,连接将尝试在没有凭据的情况下继续。"

但是我怎么知道这个错误是否会出现在 401 状态之后呢?可以在其他类型的请求之后出现吗?

【问题讨论】:

【参考方案1】:

要检查 401 错误,您可以这样做:

if (error != nil && error.code == NSURLErrorUserCancelledAuthentication) 
    // do something for 401 error

希望对你有所帮助

【讨论】:

【参考方案2】:

为了获得 401 状态码,我认为您需要实现协议 NSURLConnectionDelegate,然后是 connection:didReceiveAuthenticationChallenge:

因此,您还需要传递委托,可能使用 [NSURLConnection connectionWithRequest:request delegate:self]

而且,如果您不尝试实施身份验证质询,我宁愿始终返回 200 状态码,但 json 内容不同。

希望对你有帮助。

【讨论】:

如我所见,connection:didReceiveAuthenticationChallenge: 已被 os.x 弃用(未来可能也适用于 ios),我认为 connection:willSendRequestForAuthenticationChallenge: 也会对我有所帮助,对吗?那我从哪里得到回复呢? 我猜会得到连接的响应:didReceiveResponse:。 您可以使用connection:didReceiveResponse、connection:didReceiveData和connectionDidFinishLoading。所以,你还需要实现 NSURLConnectionDataDelegate 协议。 您是否碰巧知道代码为-1012 的connectionError 是否特定于这个东西?无论如何,可以通过其他方式得到错误吗?现在将错误视为 401 响应会容易得多,因为我有很多不同的请求。不幸的是,现在无法将来自服务器的所有响应标记为 200。 不幸的是,我认为这是设计使然 :(。我想我已经看到在 c# 上发生了相同的行为,您需要在其中强制转换异常然后获得响应。【参考方案3】:

我有一个 Web API,对于特定请求返回状态代码 200 如果一切正常,如果用户未登录,则为 401 授权令牌。如果响应状态一切正常 是 200,但如果响应状态为 401,返回代码-1012的连接错误,而响应 是零。

我遇到了完全相同的问题,REST API 调用在 Android 和 Postman 中返回 401,但在 iOS 中状态代码为 0,连接错误代码为 -1012。

您可以在this SO 帖子中找到有关此问题的更多信息。

似乎是异步和同步请求都发生的 iOS 错误(或至少是非常奇怪的方法)。

我发布此内容以防万一这可能对遇到相同问题的其他人有用。在请求之后,我在代码中管理 401 状态的方法是调用一个方法来检查每个托管的 http 状态代码。

方法接收(NSError **)error[(NSHTTPURLResponse *)response statusCode]

这是 401 部分 - 使用存储在 userInfo 结构中的错误详细信息。

// Auth failed or token problems
if (statusCode == 0 && [error userInfo] != nil) 

    // Get error detailed informations
    NSDictionary *userInfo = [error userInfo];
    NSString *errorString = [[userInfo objectForKey:NSUnderlyingErrorKey] localizedDescription];

    // If error message is of type authFailed or returns iOS code -1012 meaning auth failed
    if ([errorString containsString:@"kCFErrorDomainCFNetwork"] || [errorString containsString:@"-1012"]) 
        NSLog(@"%@ - ConnectionError - Code 401 - Cause: authentication failed, token is invalid or expired", type);
     else 
        NSLog(@"%@ - ConnectionError - Cause: generic auth error", type);
    

    // Alert user that auth failed
    ...

另一种方法是按照上面@ThuanDINH 的建议直接检查此错误代码 (-1012)。 (编辑目标 c 的答案)。

我的代码可以改成:

// Auth failed - token problems
if (statusCode == 0 && [error code] == NSURLErrorUserCancelledAuthentication) 

    // If error code is UserCanceledAuthentication
    MPLog(MPLogLevelInfo, @"%@ - ConnectionError - Cause: authentication failed, token is invalid or expired", type);

    // Alert user that auth failed
    ...

但是这样你不会处理所有其他错误(你需要打开每个 NSURLError 代码)。

Here你可以找到包含所有 NSURLError 代码列表的 SO 问题。

【讨论】:

【参考方案4】:

基于苹果文档https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/NSURLSessionConcepts/NSURLSessionConcepts.html

Note: NSURLSession does not report server errors through the error parameter. The only errors your delegate receives through the error parameter are client-side errors, such as being unable to resolve the hostname or connect to the host. The error codes are described in URL Loading System Error Codes.
Server-side errors are reported through the HTTP status code in the NSHTTPURLResponse object. For more information, read the documentation for the NSHTTPURLResponse and NSURLResponse classes.

我们需要确保不会在代码中取消会话。例如,当 previousFailureCount > 1 时,我在 didReceiveChallenge 委托方法中调用 NSURLSessionAuthChallengeCancelAuthenticationChallenge。这会抑制 401 响应并调用 didReceiveResponse。

当我将上述值更改为 NSURLSessionAuthChallengePerformDefaultHandling 时,我在 didReceiveResponse 委托方法中收到 401 Unauthorized 响应并按预期工作。

【讨论】:

以上是关于NSURLConnection 返回错误而不是响应 401的主要内容,如果未能解决你的问题,请参考以下文章

NSURLConnection 即使在重置授权标头后也会返回缓存的响应

NSURLConnection 发送 GET 请求而不是 POST 请求

NSURLConnection 是不是保留其委托?

AFNetworking 在 NSError 对象中返回响应

Laravel 路由仅在 phpunit 测试期间返回 302 而不是 404 响应

由于cors,Angular 1.5响应状态返回NULL(-1)而不是503