Использование пользовательского NSURLProtocol с UIWebView и POST запросами
В моем iOS-приложении я использую UIWebView и пользовательский протокол (с моей собственной реализацией NSURLProtocol). Я был достаточно осторожен, чтобы убедиться, что всякий раз, когда я загружаю url, я загружаю что-то вроде этого в свой UIWebView:
Myprotocol: / / myserver / mypath
И в моей реализации NSURLProtocol я беру изменяемую копию NSURLRequest, преобразую URL-адрес в http: и отправляю его на мой сервер.
Все работает для запросов HTTP GET. Проблема, с которой я сталкиваюсь, такова с почтовыми запросами. Похоже, что UIWebView неправильно кодирует данные формы в HTTPBody, если запрос использует мой пользовательский протокол.
Один обходной путь, поскольку я использую HTTPS для моих запросов сервера, заключается в том, что я регистрирую обработчик протокола для перехвата http: вместо myprotocol: и я могу конвертировать все вызовы в https: этот другой вопрос, здесь , указал мне на это решение:
Но мне интересно, есть ли какой-либо альтернативный и/или лучший способ достижения цели. то, что я хочу.
2 ответа:
Вместо того, чтобы пытаться использовать запросы POST, один обходной путь-продолжать использовать запросы GET для URL-адресов
myprotocol://
, но преобразовать их в вашей реализацииNSURLProtocol
вhttp://
и отправить запрос на ваш сервер, используя строку запроса запроса в качестве тела сообщения.Проблема с использованием GET-запросов для отправки больших объемов данных заключается в том, что где-то в цепочке запросов строка запроса может быть усечена. Это, кажется, не проблема, однако, с локально реализованными протоколы.
Я написал короткое тестовое приложение Cordova для эксперимента, и я обнаружил, что мне удалось отправить чуть более 1 MiB данных без проблем в службу Эхо-запросов HTTP http://http-echo.jgate.de/
Вот моя
startLoading
реализация:- (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]; } }
Затем я реализовал методы протокола
NSURLConnection
для пересылки к соответствующему методуNSURLProtocolClient
, но построение данных ответа в случаеTransfer-Encoding:chunked
(как и в случае ответов от http://http-echo.jgate.de/).
К сожалению, похоже, что запросы схемы
http:
иhttps:
обрабатываются несколько иначе, чем другие (в том числе пользовательские) схемы платформой Foundation Framework. Очевидно, чтоHTTPBody
иHTTPBodyStream
вызывают соответствующиеNSURLRequest
возвраты всегдаnil
для прежних. Это решается уже до вызова[NSURLProtocol canInitWithRequest]
, поэтому пользовательскаяNSURLProtocol
реализация не имеет способа повлиять на это (слишком поздно).Похоже, что для
http:
иhttps:
используется другой классNSURLRequest
, чем "стандартный". По умолчанию Реализация GnuStep этого класса возвращает всегдаnil
из вызововHTTPBody
иHTTPBodyStream
. Поэтому отдельные реализации (например, одна под PhoneGap, вероятно, часть Foundation Framework) выбираютNSURLRequest
-тип класса, основанный на схеме, предварительно согласованной сNSURLProtocol
. Для пользовательских схем вы получаетеNSURLRequest
, который возвращаетnil
как дляHTTPBody
, так и дляHTTPBodyStream
, что эффективно отключает использование метода POST (и других методов с телом) в пользовательском обработчике схемы URI.Может быть, есть способ, как чтобы повлиять на решение, какой класс
NSURLRequest
фактически используется, но в настоящее время он мне неизвестен.В качестве обходного пути вы все еще можете использовать схему
http:
илиhttps:
и принять решение в[NSURLProtocol canInitWithRequest]
на основе других критериев (например, имя хоста).