Webview/H5场景下如何使用HTTPDNS进行域名解析__最佳实践_HTTPDNS-阿里云
1. 背景说明
阿里云移动服务团队提供的HTTPDNS服务解决了传统的域名劫持、节点调度精确性以及DNS解析开销巨大等问题,为良好的移动端用户体验提供了非常精简的网络优化方案。
移动场景下基于HTML的网页呈现模型是移动应用中非常重要的技术手段,越来越多的Hybrid APP地涌现对终端的用户体验也提出了新的挑战。本文将介绍针对Webview/H5业务场景下的HTTPDNS最佳实践。
2. 实践方案
在Android以及iOS平台下,原生系统均提供了系统API以实现Webview中的网络请求拦截与自定义逻辑注入,因此我们可以通过上述拦截API拦截H5场景下的各类网络请求,在拦截函数中进行HTTPDNS解析、Header头注入以及Native网络库的调用。
2.1 Android示例
我们在HTTPDNS Android Demo github中提供了Android SDK以及HTTPDNS API接口的使用例程,这里我们通过展示Android SDK的例程演示如何在Webview/H5业务场景下使用HTTPDNS服务。注:API < 21时只能拦截到请求的url参数,其他应用层定制的参数都没法正常获取(包括method、header、body等),所以只能拦截GET请求。
// 初始化httpdnshttpdns = HttpDns.getService(getApplicationContext(), MainActivity.accountID);// 预解析热点域名httpdns.setPreResolveHosts(new ArrayList<>(Arrays.asList("www.apple.com")));webView = (WebView) this.findViewById(R.id.wv_container);webView.setWebViewClient(new WebViewClient() {@SuppressLint("NewApi")@Overridepublic WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {// 新的API可以拦截POST请求if (request != null && request.getUrl() != null && request.getMethod().equalsIgnoreCase("get")) {String scheme = request.getUrl().getScheme().trim();Map<String, String> headerFields = request.getRequestHeaders();String url = request.getUrl().toString();Log.d(TAG, "request.getUrl().toString(): " + url);// 假设我们对所有css文件的网络请求进行httpdns解析if ((scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) && request.getUrl().toString().contains(".css")) {try {URL oldUrl = new URL(url);URLConnection connection = oldUrl.openConnection();// 异步获取域名解析结果String ip = httpdns.getIpByHostAsync(oldUrl.getHost());if (ip != null) {// 通过HTTPDNS获取IP成功,进行URL替换和HOST头设置Log.d(TAG, "Get IP: " + ip + " for host: " + oldUrl.getHost() + " from HTTPDNS successfully!");String newUrl = url.replaceFirst(oldUrl.getHost(), ip);connection = (HttpURLConnection) new URL(newUrl).openConnection();// 设置HTTP请求头Host域connection.setRequestProperty("Host", oldUrl.getHost());}// copy请求header参数for (String header : headerFields.keySet()) {connection.setRequestProperty(header, headerFields.get(header));}// 注*:对于POST请求的Body数据,WebResourceRequest接口中并没有提供,这里无法处理Log.d(TAG, "ContentType: " + connection.getContentType());return new WebResourceResponse(connection.getContentType(), "UTF-8", connection.getInputStream());} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}return null;}@Overridepublic WebResourceResponse shouldInterceptRequest(WebView view, String url) {// API < 21 只能拦截URL参数if (!TextUtils.isEmpty(url) && Uri.parse(url).getScheme() != null) {String scheme = Uri.parse(url).getScheme().trim();Log.d(TAG, "url: " + url);// 假设我们对所有css文件的网络请求进行httpdns解析if ((scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) && url.contains(".css")) {try {URL oldUrl = new URL(url);URLConnection connection = oldUrl.openConnection();// 异步获取域名解析结果String ip = httpdns.getIpByHostAsync(oldUrl.getHost());if (ip != null) {// 通过HTTPDNS获取IP成功,进行URL替换和HOST头设置Log.d(TAG, "Get IP: " + ip + " for host: " + oldUrl.getHost() + " from HTTPDNS successfully!");String newUrl = url.replaceFirst(oldUrl.getHost(), ip);connection = (HttpURLConnection) new URL(newUrl).openConnection();// 设置HTTP请求头Host域connection.setRequestProperty("Host", oldUrl.getHost());}Log.d(TAG, "ContentType: " + connection.getContentType());return new WebResourceResponse(connection.getContentType(), "UTF-8", connection.getInputStream());} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}return null;}});webView.loadUrl(targetUrl);
2.2 iOS示例
HTTPDNS iOS Demo github中提供了iOS SDK以及HTTPDNS API接口的使用例程,下面是在Webview/H5业务场景下使用HTTPDNS服务的示例。
解决方案针对UIWebView,其步骤为:注册NSURLProtocol子类 -> 使用NSURLProtocol子类拦截Webview请求 -> 使用NSURLSession重新发起请求 -> 将NSURLSession请求的响应内容返回给Webview。
在Webview发起请求前注册NSURLProtocol子类,在示例的WebViewController.m中。
// 注册拦截请求的NSURLProtocol[NSURLProtocol registerClass:[WebViewURLProtocol class]];self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];[self.view addSubview:self.webView];NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://www.apple.com"]];[self.webView loadRequest:req];
在WebViewURLProtocol中拦截Webview请求,示例中假设只拦截css请求。
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {NSMutableURLRequest *mutableReq = [request mutableCopy];NSString *originalUrl = mutableReq.URL.absoluteString;NSURL *url = [NSURL URLWithString:originalUrl];// 异步接口获取IP地址NSString *ip = [[HttpDnsService sharedInstance] getIpByHostAsync:url.host];if (ip) {// 通过HTTPDNS获取IP成功,进行URL替换和HOST头设置NSLog(@"Get IP(%@) for host(%@) from HTTPDNS Successfully!", ip, url.host);NSRange hostFirstRange = [originalUrl rangeOfString:url.host];if (NSNotFound != hostFirstRange.location) {NSString *newUrl = [originalUrl stringByReplacingCharactersInRange:hostFirstRange withString:ip];NSLog(@"New URL: %@", newUrl);mutableReq.URL = [NSURL URLWithString:newUrl];[mutableReq setValue:url.host forHTTPHeaderField:@"host"];// 添加originalUrl保存原始URL[mutableReq addValue:originalUrl forHTTPHeaderField:@"originalUrl"];}}return [mutableReq copy];}
使用HTTPDNS SDK替换原请求中的HOST。
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {NSMutableURLRequest *mutableReq = [request mutableCopy];NSString *originalUrl = mutableReq.URL.absoluteString;NSURL *url = [NSURL URLWithString:originalUrl];// 异步接口获取IP地址NSString *ip = [[HttpDnsService sharedInstance] getIpByHostAsync:url.host];if (ip) {// 通过HTTPDNS获取IP成功,进行URL替换和HOST头设置NSLog(@"Get IP(%@) for host(%@) from HTTPDNS Successfully!", ip, url.host);NSRange hostFirstRange = [originalUrl rangeOfString:url.host];if (NSNotFound != hostFirstRange.location) {NSString *newUrl = [originalUrl stringByReplacingCharactersInRange:hostFirstRange withString:ip];NSLog(@"New URL: %@", newUrl);mutableReq = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:newUrl]];[mutableReq setValue:url.host forHTTPHeaderField:@"host"];// 添加originalUrl保存原始URL[mutableReq addValue:originalUrl forHTTPHeaderField:@"originalUrl"];}}return [mutableReq copy];}
使用NSURLSession重新发起请求。
- (void)startLoading {NSMutableURLRequest *request = [self.request mutableCopy];// 表示该请求已经被处理,防止无限循环[NSURLProtocol setProperty:@(YES) forKey:protocolKey inRequest:request];[self startRequest];}
将NSURLSession请求返回的响应信息传递给Webview。
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {NSLog(@"receive response: %@", response);// 获取原始URLNSString* originalUrl = [dataTask.currentRequest valueForHTTPHeaderField:@"originalUrl"];if (!originalUrl) {originalUrl = response.URL.absoluteString;}if ([response isKindOfClass:[NSHTTPURLResponse class]]) {NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;NSURLResponse *retResponse = [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:originalUrl] statusCode:httpResponse.statusCode HTTPVersion:(__bridge NSString *)kCFHTTPVersion1_1 headerFields:httpResponse.allHeaderFields];[self.client URLProtocol:self didReceiveResponse:retResponse cacheStoragePolicy:NSURLCacheStorageNotAllowed];} else {NSURLResponse *retResponse = [[NSURLResponse alloc] initWithURL:[NSURL URLWithString:originalUrl] MIMEType:response.MIMEType expectedContentLength:response.expectedContentLength textEncodingName:response.textEncodingName];[self.client URLProtocol:self didReceiveResponse:retResponse cacheStoragePolicy:NSURLCacheStorageNotAllowed];}completionHandler(NSURLSessionResponseAllow);}- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {[self.client URLProtocol:self didLoadData:data];}- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {if (error) {[self.client URLProtocol:self didFailWithError:error];} else {[self.client URLProtocolDidFinishLoading:self];}}
立即试用HTTPDNS服务,点击此处。
最后更新:2016-11-23 16:04:07
上一篇:
如何利用HTTPDNS降低DNS解析开销__最佳实践_HTTPDNS-阿里云
下一篇:
HTTPDNS域名解析场景下如何使用Cookie?__最佳实践_HTTPDNS-阿里云
服务__系统管理_用户指南(Linux)_数据管理-阿里云
视频:3分钟看懂OSS__数据操作常见问题_产品使用问题_对象存储 OSS-阿里云
查询订单__订单服务接口_API文档_域名-阿里云
图片URL规则__接入图片服务_老版图片服务手册_对象存储 OSS-阿里云
提交媒体信息作业__媒体信息接口_API使用手册_媒体转码-阿里云
还在害怕wannacry?成都锐盾、阿里云推商业软件免费试用
删除NAT网关__NAT网关相关接口_API 参考_云服务器 ECS-阿里云
配置SQL条件过滤迁移数据__数据迁移_用户指南_数据传输-阿里云
AutoSnapshotPolicyType__数据类型_API 参考_云服务器 ECS-阿里云
查询流控历史__直播流操作接口_API 手册_CDN-阿里云
相关内容
常见错误说明__附录_大数据计算服务-阿里云
发送短信接口__API使用手册_短信服务-阿里云
接口文档__Android_安全组件教程_移动安全-阿里云
运营商错误码(联通)__常见问题_短信服务-阿里云
设置短信模板__使用手册_短信服务-阿里云
OSS 权限问题及排查__常见错误及排除_最佳实践_对象存储 OSS-阿里云
消息通知__操作指南_批量计算-阿里云
设备端快速接入(MQTT)__快速开始_阿里云物联网套件-阿里云
查询API调用流量数据__API管理相关接口_API_API 网关-阿里云
使用STS访问__JavaScript-SDK_SDK 参考_对象存储 OSS-阿里云