Using a custom NSURLProtocol with UIWebView and POST requests

两盒软妹~` 提交于 2019-11-30 19:01:15

Instead of trying to use POST requests, one work around is to continue using GET requests for myprotocol:// URLs, but transform them in your NSURLProtocol implementation to an http:// and POST request to your server using the request query string as the body of the POST.

The worry with using GET requests to send large amounts of data is that somewhere along the request chain, the request line might get truncated. This appears to not be a problem, however, with locally-implemented protocols.

I wrote a short Cordova test app to experiment and I found that I was able to send through a little over 1 MiB of data without trouble to the HTTP request echoing service http://http-echo.jgate.de/

Here is my startLoading implementation:

- (void)startLoading {
    NSURL *url = [[self request] URL];
    NSString *query = [url query];
    // Create a copy of `url` without the query string.
    url = [[[NSURL alloc] initWithScheme:@"http" host:@"http-echo.jgate.de" path:[url path]] autorelease];
    NSMutableURLRequest *newRequest = [NSMutableURLRequest requestWithURL:url];
    [newRequest setHTTPMethod:@"POST"];
    [newRequest setAllHTTPHeaderFields:[[self request] allHTTPHeaderFields]];
    [newRequest addValue:@"close" forHTTPHeaderField:@"Connection"];
    [newRequest addValue:@"application/x-www-form-urlencoded;charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
    [newRequest setHTTPBody:[query dataUsingEncoding:NSUTF8StringEncoding]];
    urlConnection = [[NSURLConnection alloc] initWithRequest:newRequest delegate:self];
    if (urlConnection) {
        receivedData = [[NSMutableData data] retain];
    }
}

I then implemented the NSURLConnection protocol methods to forward to the appropriate NSURLProtocolClient method, but building up the response data in the case of Transfer-Encoding:chunked (as is the case for responses from http://http-echo.jgate.de/).

Unfortunately it looks like that http: and https: scheme requests are handled slightly differently than other (including custom) schemes by Foundation Framework. Obviously HTTPBody and HTTPBodyStream calls on relevant NSURLRequest returns always nil for former ones. This is decided already prior call of [NSURLProtocol canInitWithRequest] therefore custom NSURLProtocol implementation has no way of influencing that (it is too late).

It seems that different NSURLRequest class is used for http: and https: than 'a default one'. Default GnuStep implementation of this class returns always nil from HTTPBody and HTTPBodyStream calls. Therefore particular implementations (e.g. one under PhoneGap, likely part of Foundation Framework) choose NSURLRequest-type of class based on scheme prior consulting that with NSURLProtocol. For custom schemes, you get NSURLRequest that returns nil for both HTTPBody and HTTPBodyStream which effectively disables use of POST method (and other methods with body) in custom URI scheme handler.

Maybe there is a way how to influence decision of which NSURLRequest class is actually used but it is currently unknown to me.

As a workaround, you can still use http: or https: scheme and decide in [NSURLProtocol canInitWithRequest] based on other criteria (e.g. host name).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!