iOS URL Loading System / HTTP 重定向 认识与学习

摘要:
但在判断链接资源时,由于某些操作请求是本地化的,例如,请求服务器使用http,服务器在返回的响应中写入所请求资源的正确URL,然后我们将继续请求商城中订单列表的URL并正常显示。这样做的优点是服务器可以动态修改订单列表的链接。首先,这种注册NSURL协议的方法可以解决以下问题:

一个朋友问了我一个问题,需求是这样的:他要用本地的H5资源 替换 链接资源,  但是判断链接资源时候 因为一些操作请求本地化了之后  一些操作比如请求服务器使用的是http开头,然而本地资源一直是以file://开头, 这样的

然后 shouldStart 方法中 的request(post请求)  body  是空的,  这样就无法到底知道是哪个链接了.于是就不能触发相应的资源方法.
 
我思考好一阵子,起初 我以为这个就是应该是 用 shouldStart 协议 根据 url 判断 是否需要本地处理. 最终
解决办法是 “通过URL加载系统 (URL Loading System),拦截需要的h5资源,把本地数据直接展示
 
原因:
(1)利用UIWebView来处理回调地址,或者做拦截操作 一般都是得请求shouldStart协议中,甚至 需要知道请求回调结果 
 
就是HTTP重定向的问题! 之前 真的没有用过这个NSURLProtocol 协议去处理过问题.今天学习一下.
 
HTTP重定向: (在网上查资料 了解到的一个比较系统的解释)
Redirect(客户端重定向 我们这里研究的是)
标准意义上的“重定向”指的是HTTP重定向,它是HTTP协议规定的一种机制。这种机制是这样工作的:当client向server发送一个请求,要求获取一个资源时,在server接收到这个请求后发现请求的这个资源实际存放在另一个位置,于是server在返回的response中写入那个请求资源的正确的URL,并设置reponse的状态码为301(表示这是一个要求浏览器重定向的response),当client接受到这个response后就会根据新的URL重新发起请求。重定向有一个典型的特症,即,当一个请求被重定向以后,最终浏览器上显示的URL往往不再是开始时请求的那个URL了。这就是重定向的由来。
 
我们在实际开发过程中 遇到过这种 服务端重定向的情况,就是 和第三方电商合作,需要展示一个订单列表,就是我们作为客户前端 向服务端发起请求,然后服务端发现对应资源在商城里,然后回调结果是301 /也有302,然后会继续请求商城的订单列表的url 然后就正常展示了.
我们这么做的好处是: 不需要在客户端写死 订单列表的请求,服务端可以动态修改订单列表的链接.
 
首先是这个注册 NSURLProtocol协议方法可以解决的问题:
 
笼统就是 上面提到的"Redirect(客户端重定向)",对上层的 NSURLRequest 进行拦截,然后按需求响应操作.
NSURLProtocol具体可以做:

(1)如果需要,可以对html页面中的图片做本地化处理 

(2)Mock假的response

(3)对请求头做规范化处理

(4)在上层应用不感知情况下,实现一套代理机制

(5)过滤请求、响应中敏感信息

(6)对已有协议做改进、补充处理

这些是网上查得到的,总得来说就是拦截请求时候 可以高度自定义请求方式, 拦截请求结果 高度自定义处理方法. 在实际开发中,根据具体需求处理.

我写了一个 实例 参考SectionDemo 的CustomUrlProtocol

//
//  MyURLProtocol.h
//  NSURLProtocolExample
//
//  Created by HF on 2017/5/3.
//  Copyright © 2017年 HF. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface MyURLProtocol : NSURLProtocol

@property (nonatomic, strong) NSMutableData *mutableData;
@property (nonatomic, strong) NSURLResponse *response;

@end
//
//  MyURLProtocol.m
//  NSURLProtocolExample
//
//  Created by HF on 2017/5/3.
//  Copyright © 2017年 HF. All rights reserved.
//

#import "MyURLProtocol.h"
#import "AppDelegate.h"
#import "CachedURLResponseModel+CoreDataProperties.h"


@interface MyURLProtocol () <NSURLConnectionDelegate>

@property (nonatomic, strong) NSURLConnection *connection;

@end

@implementation MyURLProtocol

/**
 *  是否拦截处理指定的请求
 *
 *  @param request 指定的请求
 *
 *  @return 返回YES表示要拦截处理,返回NO表示不拦截处理
 */
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    static NSUInteger requestCount = 0;
    NSLog(@"Request #%lu: URL = %@", (unsigned long)requestCount++, request);
    //看看是否已经处理过了,防止无限循环
    if ([NSURLProtocol propertyForKey:@"MyURLProtocolHandledKey" inRequest:request]) {
        return NO;
    }
    return YES;
}

#pragma mark - NSURLProtocol

/**
 重写这个协议 目的是按需求条件筛选出目标请求,同时对目标request进行进一步完整包装与定义
 
 @param request request
 @return NSURLRequest
 */
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
    NSMutableURLRequest *mutableReqeust = [request mutableCopy];
    mutableReqeust = [self redirectHostInRequset:mutableReqeust];
    return mutableReqeust;

    //return request;
}

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
    return [super requestIsCacheEquivalent:a toRequest:b];
}

- (void)startLoading {
    //如果想直接返回缓存的结果,构建一个CachedURLResponse对象
    
    // 1.
    CachedURLResponseModel *cachedResponse = [self cachedResponseForCurrentRequest];
    if (cachedResponse) {
        NSLog(@"serving response from cache");
        
        // 2.
        NSData *data = cachedResponse.data;
        NSString *mimeType = cachedResponse.mimeType;
        NSString *encoding = cachedResponse.encoding;
        
        // 3.
        NSURLResponse *response = [[NSURLResponse alloc] initWithURL:self.request.URL
                                                            MIMEType:mimeType
                                               expectedContentLength:data.length
                                                    textEncodingName:encoding];
        
        // 4.
        [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
        [self.client URLProtocol:self didLoadData:data];
        [self.client URLProtocolDidFinishLoading:self];
    } else {
        // 5.
        NSLog(@"serving response from NSURLConnection");
        
        NSMutableURLRequest *newRequest = [self.request mutableCopy];
        //标记"tag",防止无限循环
        [NSURLProtocol setProperty:@YES forKey:@"MyURLProtocolHandledKey" inRequest:newRequest];
        
        self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
    }
}

- (void)stopLoading {
    [self.connection cancel];
    self.connection = nil;
}

#pragma mark - NSURLConnectionDelegate

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    
    self.response = response;
    self.mutableData = [[NSMutableData alloc] init];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.client URLProtocol:self didLoadData:data];
    
    [self.mutableData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    [self.client URLProtocolDidFinishLoading:self];
    
    [self saveCachedResponse];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [self.client URLProtocol:self didFailWithError:error];
}

#pragma mark -- private

+(NSMutableURLRequest*)redirectHostInRequset:(NSMutableURLRequest*)request
{
    if ([request.URL host].length == 0) {
        return request;
    }
    
    NSString *originUrlString = [request.URL absoluteString];
    NSString *originHostString = [request.URL host];
    NSRange hostRange = [originUrlString rangeOfString:originHostString];
    if (hostRange.location == NSNotFound) {
        return request;
    }
    
    //定向到bing搜索主页
    NSString *ip = @"cn.bing.com";
    
    // 替换host
    NSString *urlString = [originUrlString stringByReplacingCharactersInRange:hostRange withString:ip];
    NSURL *url = [NSURL URLWithString:urlString];
    request.URL = url;
    
    return request;
}

- (void)saveCachedResponse {
    NSLog(@"saving cached response");
    
   // if (![self.request.URL.absoluteString isEqualToString:@"cn.bing.com"]) return;
    
    // 1.
    AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
    NSManagedObjectContext *context = delegate.managedObjectContext;
    
    // 2.
    CachedURLResponseModel *cachedResponse = [NSEntityDescription insertNewObjectForEntityForName:@"CachedURLResponseModel"inManagedObjectContext:context];
    cachedResponse.data = self.mutableData;
    cachedResponse.url = self.request.URL.absoluteString;
    cachedResponse.timestamp = [NSDate date];
    cachedResponse.mimeType = self.response.MIMEType;
    cachedResponse.encoding = self.response.textEncodingName;
    
    // 3.
    NSError *error;
    BOOL const success = [context save:&error];
    if (!success) {
        NSLog(@"Could not cache the response.");
    }
}

- (CachedURLResponseModel *)cachedResponseForCurrentRequest {
    // 1.
    AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
    NSManagedObjectContext *context = delegate.managedObjectContext;
    
    // 2.
    NSFetchRequest *fetchRequest = [CachedURLResponseModel fetchRequest];
    
    // 3.
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"url == %@", self.request.URL.absoluteString];
    [fetchRequest setPredicate:predicate];
    
    // 4.
    NSError *error;
    NSArray *result = [context executeFetchRequest:fetchRequest error:&error];
    
    // 5.
    if (result && result.count > 0) {
        return result[0];
    }
    
    return nil;
}
@end

然后在需要的请求前注册这个类

[NSURLProtocol registerClass:[MyURLProtocol class]];

请求结束 注销这个类

[NSURLProtocol unregisterClass:[MyURLProtocol class]];

MyURLProtocol 里面 针对目标请求 具体按需处理

这里 我举得例子 是:

首先 :请求 "https://www.raywenderlich.com" 使用 "MyURLProtocol"  注册 拦截 该请求 然后重定向到 "cn.bing.com"上

其次:对重定向 对象 添加缓存

注意:

这里只是模拟过程  没有特此针对判断具体链接,真正使用的时候大家一定要逻辑严谨,并且根据具体需求要做适当优化,才能灵活达到举一反三目的。 

参考:

1. http://blog.csdn.net/xanxus46/article/details/51946432
2 .http://blog.csdn.net/bluishglc/article/details/7953614
3.http://www.molotang.com/
4.https://www.raywenderlich.com/59982/nsurlprotocol-tutorial

免责声明:文章转载自《iOS URL Loading System / HTTP 重定向 认识与学习》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇sql select 如何定义自增列?Oracle基础 存储过程和事务下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

UI自动化自动验证登录滑动拼图验证码

用于UI自动化登录拼图滑动验证码 1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # @Time : 2019/11/15 14:32 4 # @Site : 5 # @File : puzzleCaptcha.py 6 # @Software: PyCha...

Django Restful Framework

你在浏览器中输入了一个地址的时候发生了什么事情? 1.HOST 2.DNS 3.HTTP/HTTPS协议 发送一个协议 4.进入了实现了WSGI协议的服务器(wsgiref uwsgi(C语言实现,多线程,多进程,PHP,TOMCAT)) 5.请求进入Django 6. 前后端不分离:中间件->路由分发->对应的视图函数->找到模板,渲...

elasticsearch安装ansj分词器

1、概述    elasticsearch用于搜索引擎,需要设置一些分词器来优化索引。常用的有ik_max_word: 会将文本做最细粒度的拆分、ik_smart: 会做最粗粒度的拆分、ansj等。    ik下载地址: https://github.com/medcl/elasticsearch-analysis-ik/releases    ansj下...

django使用ldap认证

pip3 installdjango-auth-ldap python-ldap urls.py, from app0104 importviews urlpatterns =[ url(r'^admin/', admin.site.urls), url(r'^loginauth/', views.loginauth), url(r...

.Net Task&amp;lt;T&amp;gt;的一种比较神奇的卡死情况(Wait/Result卡死, await能得到结果)

出现的环境.Net4.0 + WebApi1(4.0.30506.0) +Microsoft.Bcl.Async.1.0.168 自己死活看不出原因, 分享出来给大家看看,希望有人能找到问题的关键 出现错误的是下面这两个模块 下面的CorsMessageHandler,抄的http://www.cnblogs.com/artech/p/cors-4-asp...

MongoDB update数据语法

在前面的文章“mongodb 查询的语法”里,我介绍了Mongodb的常用查询语法,Mongodb的update操作也有点复杂,我结合自己的使用经验,在这里介绍一下,给用mongodb的朋友看看,也方便以后自己用到的时候查阅:注:在这篇文章及上篇文章内讲的语法介绍都是在mongodb shell环境内的,和真正运用语言编程(如java,php等)使用时,在...