UIWebView 捕获响应标头

Posted

技术标签:

【中文标题】UIWebView 捕获响应标头【英文标题】:UIWebView capturing the response headers 【发布时间】:2013-03-18 12:11:06 【问题描述】:

我搜索/谷歌了很多,但无法获得有关如何在UIWebview 中捕获 HTTP 响应标头的答案。假设我在应用程序启动时将用户重定向到 UIWebview 中的注册网关(它已经处于活动状态),当用户完成注册时,应通知应用程序,并在注册时分配给用户的成功唯一 ID,该 ID 在 HTTP 响应标头中传回.

是否有任何直接的方法可以使用UIWebview 捕获/打印 HTTP 响应标头?

【问题讨论】:

我找到了一种方法——唯一的问题是无法知道响应是从哪里创建的。可以吗,还是我应该进一步挖掘? 【参考方案1】:

没有办法从UIWebView 获取响应对象(为此向苹果提交一个错误,我说)

但有两种解决方法

1) 通过共享的 NSURLCache

- (void)viewDidAppear:(BOOL)animated 
    NSURL *u = [NSURL URLWithString:@"http://www.google.de"];
    NSURLRequest *r = [NSURLRequest requestWithURL:u];
    [self.webView loadRequest:r];


- (void)webViewDidFinishLoad:(UIWebView *)webView 
    NSCachedURLResponse *resp = [[NSURLCache sharedURLCache] cachedResponseForRequest:webView.request];
    NSLog(@"%@",[(NSHTTPURLResponse*)resp.response allHeaderFields]);

@end

如果这对你有用,这是理想的


否则

    您可以完全使用 NSURLConnection,然后只需使用您下载的 NSData 来提供 UIWebView :)

这将是一个糟糕的解决方法! (正如理查德在 cmets 中指出的那样。)它确实有很大的缺点,你必须看看它是否是你的情况下的有效解决方案

NSURL *u = [NSURL URLWithString:@"http://www.google.de"];
NSURLRequest *r = [NSURLRequest requestWithURL:u];
[NSURLConnection sendAsynchronousRequest:r queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *resp, NSData *d, NSError *e) 
    [self.webView loadData:d MIMEType:nil textEncodingName:nil baseURL:u];
    NSLog(@"%@", [(NSHTTPURLResponse*)resp allHeaderFields]);
];

【讨论】:

它也不干净,因为 web 视图不能使用 AJAX 并让你拦截它,而且你不会像使用 web 视图那样获得增量负载。 缓存不适用于 POST 请求(甚至是禁用缓存的 GET 请求)。通过覆盖 NSURLResponse 的特定构造函数,我能够成功转储 WebView(甚至是 AJAX 请求)发出的每个 URL 请求。 上帝使用建设性的和好的! :P 顺便说一句,这就是我写 如果这对你有用,那太好了,因为我知道它不会总是有效! @RichardJ.RossIII 你是说为NSURLResponse 调配构造函数?【参考方案2】:

我喜欢 Objective-c 运行时。有什么你想做但没有 API 的吗? DM;人力资源部。

好的,更严肃地说,这是解决此问题的方法。它将捕获从CFNetwork 发起的每个 URL 响应,这是 UIWebView 在幕后碰巧使用的。它还将捕获 AJAX 请求、图像加载等。

为此添加过滤器可能就像对标头的内容执行正则表达式一样简单。

@implementation NSURLResponse(webViewHack)

static IMP originalImp;

static char *rot13decode(const char *input)

    static char output[100];

    char *result = output;

    // rot13 decode the string
    while (*input) 
        if (isalpha(*input))
        
            int inputCase = isupper(*input) ? 'A' : 'a';

            *result = (((*input - inputCase) + 13) % 26) + inputCase;
        
        else 
            *result = *input;
        

        input++;
        result++;
    

    *result = '\0';
    return output;


+(void) load 
    SEL oldSel = sel_getUid(rot13decode("_vavgJvguPSHEYErfcbafr:"));

    Method old = class_getInstanceMethod(self, oldSel);
    Method new = class_getInstanceMethod(self, @selector(__initWithCFURLResponse:));

    originalImp = method_getImplementation(old);
    method_exchangeImplementations(old, new);


-(id) __initWithCFURLResponse:(void *) cf 
    if ((self = originalImp(self, _cmd, cf))) 
        printf("-[%s %s]: %s", class_getName([self class]), sel_getName(_cmd), [[[self URL] description] UTF8String]);

        if ([self isKindOfClass:[NSHTTPURLResponse class]])
        
            printf(" - %s", [[[(NSHTTPURLResponse *) self allHeaderFields] description] UTF8String]);
        

        printf("\n");
    

    return self;


@end

【讨论】:

苹果不赞成使用私有api @Daij-Djan 这里没有使用私有 API。每个方法调用在 Objective-C 运行时都是公共的。我只是引用了一个通常隐藏的选择器。我在一个通过审核的应用程序中使用了同样的想法,如果你真的很偏执,只需 XOR 选择器字符串。 @Daij-Djan 没有文档!=私有。 NSExpression 恰好有一个构造函数 (expressionWithFormat:) 位于公共标头中,但无论出于何种原因都没有任何文档。然而,这并不意味着它是私有的。 @MuhammadZeeshan 实际上没有 - 这不是完整的文件。您需要为此包含 objc/runtime,而不是 objc/message。 @Rivera 您应该在调用之前将函数指针转换为正确的函数签名,我相信来自 IMP 的签名从 typedef id (*IMP)(id, SEL, ...) 更改为 typedef id (*IMP)() 由于担心先前的签名缺乏安全性新的 ABI(主要是 ARM64)。【参考方案3】:

如果您想要@Richard J. Ross III 编写的更高级的 API 代码,您需要继承 NSURLProtocol。

NSURLProtocol 是一个处理 URL 请求的对象。因此,您可以将它用于在 NSHipster 和 Ray Wenderlich 上更好地描述的特定任务,其中包括您从响应中获取 HTTP 标头的情况。

代码

从 NSURLProtocol 创建一个新的子类,你的 .h 文件应该如下所示:

@interface CustomURLProtocol : NSURLProtocol <NSURLConnectionDelegate>

@property (nonatomic, strong) NSURLConnection *connection;

@end

你的 .m 文件应该有这些方法来处理你想要的

@implementation CustomURLProtocol

+ (BOOL)canInitWithRequest:(NSURLRequest *)request 
    // Here you can add custom filters to init or not specific requests
    return YES;


+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request 
    // Here you can modify your request
    return request;


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


- (void)startLoading 
    // Start request
    self.connection = [NSURLConnection connectionWithRequest:self.request delegate:self];


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


#pragma mark - Delegation

#pragma mark NSURLConnectionDelegate

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 
    if ([response isKindOfClass:[NSHTTPURLResponse class]]) 
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        
        // Here we go with the headers
        NSDictionary *allHeaderFields = [httpResponse allHeaderFields];
    
    
    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];


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


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


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

最后要做的就是将此协议注册到加载系统,这在 AppDelegate 上很容易实现:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
    [NSURLProtocol registerClass:[CustomURLProtocol class]];
    return YES;

【讨论】:

【参考方案4】:

NSHTTPURLResponse 有类似的方法

- (NSDictionary *)allHeaderFields

更多信息 https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSHTTPURLResponse_Class/Reference/Reference.html#//apple_ref/occ/cl/NSHTTPURLResponse

编辑:对不起,我没有考虑UIWebView。如果您使用NSURLConnection,我的解决方案有效

但是,如果您向 webview 提供 NSURLConnection,那么您就有机会捕获包括响应标头在内的连接。

【讨论】:

你如何从网络视图中获取它.. 就是这样:) NSURLConnection 很糟糕,正如理查德指出的那样。 一开始也想用它。 ://

以上是关于UIWebView 捕获响应标头的主要内容,如果未能解决你的问题,请参考以下文章

如何在Swift中设置UIWebView的“User-Agent”标头

如何捕获包含所有内容的 UIWebview 的屏幕截图?

如何以编程方式“捕获”对 UIWebView 的后向和转发请求?

无法在 UIWebView IOS 5 中捕获触摸

响应者链(UItableViewCell 内的 UIWebView)Objective-C

UIWebView 没有响应