iOS networking(三) http异步请求和https认证

异步请求和异步队列请求不太相似,同步请求和异步队列请求都是调用的NSURLConnection的静态方法,而异步请求需要实例化一个NSURLConnection对象,并通过NSURLConnectionDelegate,NSURLConnectionDataDelegate,NSURLConnectionDownloadDelegate三个委托实现 对请求声明周期中的各个事件进行回调。

  • NSURLConnectionDelegate:主要处理https等加密认证
  • NSURLConnectionDataDelegate: 请求成功,失败,获取数据,上传进度,缓存等委托
  • NSURLConnectionDownloadDelegate: 下载相关的委托,成功,失败,数据等等。

异步请求和同步请求与异步队列请求相比,可以实现耕地的功能,如上传下载的进度,安全认证,取消和暂停,数据流等等

普通http请求

普通http请求,使用NSURLConnection示例对象,发送request,通过NSURLConnectionDataDelegate获取数据和请求状态

发送数据

#pragma mark -网络请求

- (void)nornalHttpRequest{
    NSString *urlString = @"http://localhost:8001/";
    //stringByAddingPercentEncodingWithAllowedCharacters URLQueryAllowedCharacterSet主要用于url查询字符串编码
    NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];
    NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
    NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
    //发出request
    [connection start];
}

数据和请求状态的主要委托

//请求失败
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
    NSLog(@"=================didFailWithError=================");
    NSLog(@"error:%@",error);
}

//重定向
- (nullable NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(nullable NSURLResponse *)response{
    NSLog(@"=================request redirectResponse=================");
    NSLog(@"request:%@",request);
    return request;
}

//接收响应
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    NSLog(@"=================didReceiveResponse=================");
    NSHTTPURLResponse *resp = (NSHTTPURLResponse *)response;
    NSLog(@"response:%@",resp);
}

//接收响应
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    NSLog(@"=================didReceiveData=================");
    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
    NSLog(@"data:%@",dic);
}


//完成请求
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    NSLog(@"=================connectionDidFinishLoading=================");
}


调用nornalHttpRequest方法后可以在控制台看到接收到的数据

2016-02-05 16:16:20.695 network-demo[31610:4130315] =================request redirectResponse=================
2016-02-05 16:16:20.695 network-demo[31610:4130315] request:<NSURLRequest: 0x7f9d115b0850> { URL: http://localhost:8001/ }
2016-02-05 16:16:20.877 network-demo[31610:4130315] =================didReceiveResponse=================
2016-02-05 16:16:20.877 network-demo[31610:4130315] response:<NSHTTPURLResponse: 0x7f9d1171c7e0> { URL: http://localhost:8001/ } { status code: 200, headers {
    Connection = "keep-alive";
    "Content-Type" = "text/json";
    Date = "Fri, 05 Feb 2016 08:16:20 GMT";
    "Transfer-Encoding" = Identity;
} }
2016-02-05 16:16:20.877 network-demo[31610:4130315] =================didReceiveData=================
2016-02-05 16:16:20.877 network-demo[31610:4130315] data:{
    age = 20;
    name = xxx;
}
2016-02-05 16:16:20.878 network-demo[31610:4130315] =================connectionDidFinishLoading=================

值得注意的是 =================request redirectResponse================= 为什么会出现重定向? 这是因为在我测试中,并不是第一次发起请求,而请求内容并没有发生变化,所以走了缓存,就会进入redirect重定向的委托。重定向和request设置的缓存策略有很大的关系。

NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];

https请求

https协议和http协议的区别就在于https在建立tcp连接之后,有一段相互认证的过程,

HTTPS:当客户端第一次发送请求的时候,服务器会返回一个包含公钥的受保护空间(也成为证书),当我们发送请求的时候,公钥会将请求加密再发送给服务器,服务器接到请求之后,用自带的私钥进行解密,如果正确再返回数据。这就是 HTTPS 的安全性所在

https的认证过程,在NSURLConnection中使用NSURLConnectionDelegate中的委托完成,简单的说我们获取到保护控件的证书后,验证证书,并授信证书,就可以请求https资源了,更详细的过程我也还在探索,以后再补充。

在之前实现异步http请求的基础上,我们测试请求github获取用户信息https的api,需要现实线委托,然后请求,代码如下:

//https请求-github获取用户信息
- (void)githubUserInfo{
    //string 转 url编码
    NSString *urlString = @"https://api.github.com/users/coolnameismy";
    NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];
    NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
    NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
    [connection start];
}

#pragma mark -https认证
-(BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection{
    NSLog(@"=================connectionShouldUseCredentialStorage=================");
    return true;
}
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
    NSLog(@"=================willSendRequestForAuthenticationChallenge=================");
    NSLog(@"didReceiveAuthenticationChallenge %@ %zd", [[challenge protectionSpace] authenticationMethod], (ssize_t) [challenge previousFailureCount]);

    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
        NSLog(@"是服务器信任的证书:%@",challenge.protectionSpace.authenticationMethod);
        //通过认证
        [[challenge sender]  useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
        [[challenge sender]  continueWithoutCredentialForAuthenticationChallenge: challenge];
    }
}

请求成功打印出的日志

2016-02-09 21:01:42.102 network-demo[10032:403483] =================request redirectResponse=================
2016-02-09 21:01:42.103 network-demo[10032:403483] request:<NSURLRequest: 0x7fa47b002ef0> { URL: https://api.github.com/users/coolnameismy }
2016-02-09 21:01:42.103 network-demo[10032:403483] =================connectionShouldUseCredentialStorage=================
2016-02-09 21:01:42.766 network-demo[10032:403483] =================willSendRequestForAuthenticationChallenge=================
2016-02-09 21:01:42.766 network-demo[10032:403483] didReceiveAuthenticationChallenge NSURLAuthenticationMethodServerTrust 0
2016-02-09 21:01:42.767 network-demo[10032:403483] 是服务器信任的证书:NSURLAuthenticationMethodServerTrust
2016-02-09 21:01:43.459 network-demo[10032:403483] =================didReceiveResponse=================
2016-02-09 21:01:43.460 network-demo[10032:403483] response:<NSHTTPURLResponse: 0x7fa478ee29a0> { URL: https://api.github.com/users/coolnameismy } { status code: 200, headers {
    "Access-Control-Allow-Credentials" = true;
    "Access-Control-Allow-Origin" = "*";
    "Access-Control-Expose-Headers" = "ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval";
    "Cache-Control" = "public, max-age=60, s-maxage=60";
    "Content-Encoding" = gzip;
    "Content-Security-Policy" = "default-src 'none'";
    "Content-Type" = "application/json; charset=utf-8";
    Date = "Tue, 09 Feb 2016 12:55:57 GMT";
    Etag = "W/\"f1434609a610832d68fb4fef98e43f48\"";
    "Last-Modified" = "Tue, 26 Jan 2016 16:02:40 GMT";
    Server = "GitHub.com";
    Status = "200 OK";
    "Strict-Transport-Security" = "max-age=31536000; includeSubdomains; preload";
    "Transfer-Encoding" = Identity;
    Vary = "Accept, Accept-Encoding";
    "X-Content-Type-Options" = nosniff;
    "X-Frame-Options" = deny;
    "X-GitHub-Media-Type" = "github.v3";
    "X-GitHub-Request-Id" = "7751919E:C8D5:8399087:56B9E1DC";
    "X-RateLimit-Limit" = 60;
    "X-RateLimit-Remaining" = 59;
    "X-RateLimit-Reset" = 1455026157;
    "X-Served-By" = 139317cebd6caf9cd03889139437f00b;
    "X-XSS-Protection" = "1; mode=block";
} }
2016-02-09 21:01:43.461 network-demo[10032:403483] =================didReceiveData=================
2016-02-09 21:01:43.463 network-demo[10032:403483] data:{
    "avatar_url" = "https://avatars.githubusercontent.com/u/5010799?v=3";
    bio = "<null>";
    blog = "http://liuyanwei.jumppo.com";
    company = ZTE;
    "created_at" = "2013-07-15T06:29:49Z";
    email = "coolnameismy@hotmail.com";
    "events_url" = "https://api.github.com/users/coolnameismy/events{/privacy}";
    followers = 207;
    "followers_url" = "https://api.github.com/users/coolnameismy/followers";
    following = 41;
    "following_url" = "https://api.github.com/users/coolnameismy/following{/other_user}";
    "gists_url" = "https://api.github.com/users/coolnameismy/gists{/gist_id}";
    "gravatar_id" = "";
    hireable = 1;
    "html_url" = "https://github.com/coolnameismy";
    id = 5010799;
    location = "nanjing china";
    login = coolnameismy;
    name = "\U5218\U5f66\U73ae";
    "organizations_url" = "https://api.github.com/users/coolnameismy/orgs";
    "public_gists" = 0;
    "public_repos" = 23;
    "received_events_url" = "https://api.github.com/users/coolnameismy/received_events";
    "repos_url" = "https://api.github.com/users/coolnameismy/repos";
    "site_admin" = 0;
    "starred_url" = "https://api.github.com/users/coolnameismy/starred{/owner}{/repo}";
    "subscriptions_url" = "https://api.github.com/users/coolnameismy/subscriptions";
    type = User;
    "updated_at" = "2016-01-26T16:02:40Z";
    url = "https://api.github.com/users/coolnameismy";
}
2016-02-09 21:01:43.463 network-demo[10032:403483] =================connectionDidFinishLoading=================

demo


本文的demo下载

感谢收看,如果对大家有帮助,请github上follow和star,本文发布在刘彦玮的技术博客,转载请注明出处

参考阅读

Loading Disqus comments...
Table of Contents