zoukankan      html  css  js  c++  java
  • iOS URL Loading System / HTTP 重定向 认识与学习

    一个朋友问了我一个问题,需求是这样的:他要用本地的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
  • 相关阅读:
    97. Interleaving String
    96. Unique Binary Search Trees
    95. Unique Binary Search Trees II
    94. Binary Tree Inorder Traversal
    odoo many2many字段 指定打开的form视图
    docker sentry 配置文件位置
    postgres 计算时差
    postgres 字符操作补位,字符切割
    postgres判断字符串是否为时间,数字
    odoo fields_view_get
  • 原文地址:https://www.cnblogs.com/someonelikeyou/p/6705687.html
Copyright © 2011-2022 走看看