Send custom headers with UIWebView loadRequest
Asked Answered
S

4

18

I want to be able to send some extra headers with my UIWebView loadRequest method.

I have tried:

NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.reliply.org/tools/requestheaders.php"]];
[req addValue:@"hello" forHTTPHeaderField:@"aHeader"];

[self.theWebView loadRequest:req];

I have also tried subclassing the UIWebView and intercepting the - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType method.

In that method I had a block of code which looked like this:

NSMutableURLRequest *newRequest = [request mutableCopy];
for(NSString *key in [customHeaders allKeys]) {
    [newRequest setValue:[customHeaders valueForKey:key] forHTTPHeaderField:key];
}
[self loadRequest:newRequest];

But for some unknown reason it was causing the web view to not load anything (blank frame) and the error message NSURLErrorCancelled (-999) comes up (all known fixes don't fix it for me).

So I am at a loss as to what to do. How can I send a custom header along with a UIWebView request?

Many thanks!

Schalles answered 11/10, 2012 at 16:48 Comment(3)
#1025248. I have never come across this issue before So trying to see why you might be getting itWingding
yeah that's the answer I had found when I searched for the error message. But unfortunately that "fix" doesn't work. Do you have any sample code you can share with me if you've done it before... pretty please?Schalles
Have never come across this issue. Sorry :(Wingding
S
19

I found that this was the way to add headers to my UIWebView request - override this delegate method:

- (BOOL) webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType) navigationType

With this code:

BOOL headerIsPresent = [[request allHTTPHeaderFields] objectForKey:@"my custom header"]!=nil;

if(headerIsPresent) return YES;

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_async(dispatch_get_main_queue(), ^{
        NSURL *url = [request URL];
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];

        // set the new headers
        for(NSString *key in [self.customHeaders allKeys]){
            [request addValue:[self.customHeaders objectForKey:key] forHTTPHeaderField:key];
        }

        // reload the request
        [self loadRequest:request];
    });
});
return NO;
Schalles answered 12/10, 2012 at 9:33 Comment(4)
This answer as it does not handle multiple iFrames or redirects (as noted in other comments). Also, as noted in other comments, implementing your own custom NSURLProtocol is the correct way to do this.Saville
if (navigationType == UIWebViewNavigationTypeBackForward) { return YES; }Sinuation
UPDATE:Before check header,add this(maybe more type):if (navigationType != UIWebViewNavigationTypeOther && navigationType != UIWebViewNavigationTypeLinkClicked) { return YES; } If not, [self.webView goBack] will not go back.Sinuation
Hi @JustinDomnitz , do you have any example or approach, how to achieve it. As you said this doesn't work on redirections the request doesn't pass those headers forward.Devault
W
7

The answer by Thomas will not work for (most of the) webpages with multiple iFrames. The solution will load an iFrame request over the full UIWebView. E.g. in case it calls loadRequest for a Google advt. (which is in some small iFrame) the advt. is loaded all over the UIWebView & nothing else.

Wallace answered 9/1, 2013 at 17:2 Comment(4)
I agree. I ran into the same issue with requests loaded in an iFrame. Instead of following the code by Thomas, if you are sending iFrame requests, you can cast the request object from the delegate method to NSMutableURLRequest and add the header using addValue method of NSMutableURLRequest. From this post: #7913805 (see last comment on the accepted answer).Kwei
how that answer is different from this ?Wallace
It is different because it just proceeds with loading the same request. The header is just added prior to return YES. It does not load a new request.Kwei
I also faced the iFrame problem. Still, since I have full control over the embedded web site, I let it expose a list of URL patterns that should receive custom headers, and a list of URL patterns that should not. I then use the following checks (sorry for the compact format): 1) If !GET request, no headers. 2) If headers are set, no headers. 3) If should ignore headers, no headers. 4) If should add headers, add headers. 5) If iFrame request (request != document body), no headers. 6) If start page, send headers. 7) Else, no headers.Courtney
S
2

I find another way, Can use NSURLProtocol .

-(BOOL)webView:(IMYVKWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSMutableDictionary* mapObject = [NSMutableDictionary dictionary];
    mapObject[@"headers"] = request.allHTTPHeaderFields;
    mapObject[@"navigationType"] = @(navigationType);
    [webViewRequestMap setObject:mapObject forKey:request.URL.absoluteString];
    return YES;
}

webViewRequestMap is Static NSMutableDictionary*

in Your Custom NSURLProtocol code:

    @interface IMYCustomURLProtocol : NSURLProtocol
@end
@implementation IMYCustomURLProtocol 
+(void)load
{
     [NSURLProtocol registerClass:self];
}
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
    NSString* urlString = request.URL.absoluteString;
    NSDictionary* dictionary = webViewReuqestMap[urlString];
    if (dictionary)
    {
        [webViewRequestMap removeObjectForKey:urlString];
        if ([request isKindOfClass:[NSMutableURLRequest class]]) {
           [(id)request setValue:@"HAHA" forHTTPHeaderField:@"MeiYou Co.,Ltd"];
        }
    }
    return NO;
}
@end
Shirashirah answered 15/8, 2015 at 7:51 Comment(3)
This will only work if the request already is a NSMutableURLRequest - which does not cover all cases.Spinous
UIWebView reuqest all is a NSMutableURLRequestPatentor
Hi, does it add headers in redirections as well. How can my webview/ webview controller can conform to this NSURLProtocol?Devault
K
0

Here is a complete implementation using NSURLProticol. You can put this code anywhere you like (e.i. its own or add to existing source file) and it should work. The two key methods to customize are canInitWithRequest: and canonicalRequestForRequest:.

static NSString * const NSURLProtocolHandledKey = @"NSURLProtocolHandledKey";

@interface WTCURLProtocol : NSURLProtocol<NSURLSessionDelegate>
@property (atomic,strong,readwrite) NSURLSessionDataTask *task;
@property (nonatomic,strong) NSURLSession *session;
@end

@implementation WTCURLProtocol
+(void)load
{
  [NSURLProtocol registerClass:self];
}

+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
  // customize here by returning true for URLs that you want to handle
  return [request.URL.absoluteString hasPrefix:WEB_BASE_URL];
}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
  NSMutableURLRequest *newRequest = request.mutableCopy;
  [NSURLProtocol setProperty:@YES forKey:NSURLProtocolHandledKey inRequest:newRequest];

  // customize here by setting your custom headers
  [newRequest setValue:@"ABCDEFGHIJKLMNOPQRSTUVWXYZ" forHTTPHeaderField:@"API-TOKEN"];

  return newRequest;
}

- (void)startLoading
{
  NSURLSessionConfiguration *configure = [NSURLSessionConfiguration defaultSessionConfiguration];
  NSOperationQueue *queue = [[NSOperationQueue alloc] init];

  self.session  = [NSURLSession sessionWithConfiguration:configure delegate:self delegateQueue:queue];
  self.task = [self.session dataTaskWithRequest:self.request];
  [self.task resume];
}

- (void)stopLoading
{
  [self.session invalidateAndCancel];
  self.session = nil;
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
  if (error != nil) {
    [self.client URLProtocol:self didFailWithError:error];
  }else
  {
    [self.client URLProtocolDidFinishLoading:self];
  }
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
  [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];

  completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
  [self.client URLProtocol:self didLoadData:data];
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse * _Nullable))completionHandler
{
  completionHandler(proposedResponse);
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)newRequest completionHandler:(void (^)(NSURLRequest *))completionHandler
{
  NSMutableURLRequest *redirectRequest = [newRequest mutableCopy];
  [[self class] removePropertyForKey:NSURLProtocolHandledKey inRequest:redirectRequest];
  [[self client] URLProtocol:self wasRedirectedToRequest:redirectRequest redirectResponse:response];

  [self.task cancel];
  [[self client] URLProtocol:self didFailWithError:[NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]];
}

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
{
  if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
    NSURLCredential *card = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust];
    completionHandler(NSURLSessionAuthChallengeUseCredential,card);
  }
}

@end

Karolkarola answered 22/2, 2019 at 3:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.