1

我想记录从我的应用程序中启动的所有 NSURLRequests 并响应这些请求。我编写了一个自定义的 NSURLProtocol 来实现这一点。我能够捕获所有请求,但不能捕获响应。

在 canInitWithRequest 方法中,无论方法返回是/否,我都可以记录所有请求。现在我返回“是”,希望得到实际响应。

在 -startLoading 方法中,我应该通知 NSURLProtocolClient 响应和进度。我对修改/创建自己的响应不感兴趣,而是对实际响应感兴趣并想记录它。我何时何地可以找到对请求的实际响应?

我对修改 URL 加载行为不感兴趣。

我是在使用自定义协议的正确轨道上还是我需要做其他事情来记录所有请求和响应?

@implementation TestURLProtocol

+(BOOL)canInitWithRequest:(NSURLRequest *)request
{
//here i can log all the requests    
NSLog(@"TestURLProtocol : canInitWithRequest");
    return YES;
}

+(NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
    return request;
}


-(void)startLoading
{
// I don't want to create or modify response. 
//I have nothing todo with response instead I need actual response for logging purpose.
    NSURLResponse * response = [[NSURLResponse alloc] initWithURL:[self.request URL] MIMEType:@"text/html" expectedContentLength:-1 textEncodingName:@"utf8"];
    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    [[self client] URLProtocolDidFinishLoading:self];
}

-(void)stopLoading
{

}
@end
4

2 回答 2

0

使用自定义 NSURLProtocol 的更好解决方案:可以记录所有传出的 URL 请求并更改请求属性、响应和执行其他操作,例如将加密的 html 文件从磁盘加载到 webview 等。

.h 文件

#import <Foundation/Foundation.h>

@interface URLRequestLoggerProtocol : NSURLProtocol

@end

.m 文件

#import "URLRequestLoggerProtocol.h"

@interface URLRequestLoggerProtocol ()<NSURLConnectionDelegate,NSURLConnectionDataDelegate>

@property(nonatomic, strong) NSURLConnection * connection;

@end


@implementation URLRequestLoggerProtocol
@synthesize connection;

+(BOOL)canInitWithRequest:(NSURLRequest *)request
{
     //logging only HTTP and HTTPS requests which are not being logged
    NSString * urlScheme = request.URL.scheme;
    if (([urlScheme isEqualToString:@"http"] || [urlScheme isEqualToString:@"https"]) && ![[NSURLProtocol propertyForKey:@"isBeingLogged" inRequest:request] boolValue])
    {
        return YES;
    }

    return NO;

}

+(NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
    return request;
}


-(void)startLoading
{
    NSMutableURLRequest * newRequest = [self.request mutableCopy];
    [NSURLProtocol setProperty:[NSNumber numberWithBool:YES] forKey:@"isBeingLogged" inRequest:newRequest];
    self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
}

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


#pragma mark - NSURLConnectionDelegate methods

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{

    //log all things along with error and finally inform URL client that you are done

    [self.client URLProtocol:self didFailWithError:error];
}

#pragma mark - NSURLConnectionDataDelegate methods

-(NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response
{
    //start logging a request properties from here

    return request;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{


    //log the response and other things and inform client that you are done.

    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];

}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{

    //log the data and other things and inform client that you are done.

    [self.client URLProtocol:self didLoadData:data];
}


- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{

    //log the finish info and other things and inform client that you are done.

    [self.client URLProtocolDidFinishLoading:self];
}


@end
于 2014-09-02T09:59:03.517 回答
0

好吧,我放弃了自定义 NSURLProtocol 的想法。我写了一个类,根据您的需要向 NSURLConnectionDelegate、NSURLConnectionDataDelegate 等确认。此类充当所有 NSURLConnection 实例的公共委托。它有一个 id 类型的属性(取决于要求),这个属性包含创建 NSURLConnection 并且对委托回调感兴趣的对象。

URLRequestLogger 实例充当所有 NSURLConnection 实例的公共委托。每个 NSURLConnection 都有一个 URLRequestLogger 实例作为委托。

#import <Foundation/Foundation.h>


@interface URLRequestLogger : NSObject<NSURLConnectionDataDelegate>

-(id)initWithConnectionDelegate:(id<NSURLConnectionDataDelegate>)connectionDelegate;

@end

执行文件

#import "URLRequestLogger.h"

@interface URLRequestLogger ()

@property(nonatomic, assign) id<NSURLConnectionDataDelegate> connectionDelegate;

@end

@implementation URLRequestLogger

-(id)initWithConnectionDelegate:(id<NSURLConnectionDataDelegate>)connectionDelegate
{
    self = [super init];
    if (self)
    {
        _connectionDelegate = connectionDelegate;
    }

    return self;
}

//one can implement all the delegates related to NSURLConnection as per need

-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    //log reponse info
    if ([response isKindOfClass:[NSHTTPURLResponse class]])
    {
        NSHTTPURLResponse * httpResponse  = (NSHTTPURLResponse *)response;
        NSDictionary * responseHeaders = [httpResponse allHeaderFields];
        NSInteger statusCode = [httpResponse statusCode];
        //save as many info as we can
    }

    //if connectionDelegate is interested in it, inform it
    if ([self.connectionDelegate respondsToSelector:@selector(connection:didReceiveResponse:)])
    {
        [self.connectionDelegate connection:connection didReceiveResponse:response];
    }
}


-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{

   //log the error and other info

    //if connectionDelegate is interested in it, inform it
    if ([self.connectionDelegate respondsToSelector:@selector(connection:didFailWithError:)])
    {
        [self.connectionDelegate connection:connection didFailWithError:error];
    }
}

-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    //log the connection finish info
    //response time and download time etc

    //if connectionDelegate is interested in it, inform it
    if ([self.connectionDelegate respondsToSelector:@selector(connectionDidFinishLoading:)])
    {
        [self.connectionDelegate connectionDidFinishLoading:connection];
    }
}
@end

MyViewController 创建一个 NSURLConnection 并且对委托方法也很感兴趣。同时,我们要记录有关请求和响应的所有详细信息。

#import <UIKit/UIKit.h>

@interface MyViewController : UIViewController<NSURLConnectionDataDelegate>

@end

执行文件

#import "MyViewController.h"
#import "URLRequestLogger.h"

@implementation MyViewController

//MyViewController class creates a request and interested in connection delegate callbacks
//MyViewController confirms to NSURLConnectionDelegate.
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    [self sendRequest];
}

-(void)sendRequest
{
    /*
    connection delegate here would be our URLRequestLogger class instance which holds MyViewController instance
    to return the callbacks here after logging operations are finished
     */
    URLRequestLogger * requestLogger = [[URLRequestLogger alloc] initWithConnectionDelegate:self];

    [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.example.org"]] delegate:requestLogger];
}

#pragma mark - NSURLConnection delegate methods

-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    //this callback is received from URLRequestLogger after logging operation is completed

    //do something. Update UI etc...
}

-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    //this callback is received from URLRequestLogger after logging operation is completed

    //do something. Update UI etc...
}

@end
于 2014-08-21T11:56:58.357 回答