Использование пользовательского NSURLProtocol с UIWebView и POST запросами


В моем iOS-приложении я использую UIWebView и пользовательский протокол (с моей собственной реализацией NSURLProtocol). Я был достаточно осторожен, чтобы убедиться, что всякий раз, когда я загружаю url, я загружаю что-то вроде этого в свой UIWebView:

Myprotocol: / / myserver / mypath

И в моей реализации NSURLProtocol я беру изменяемую копию NSURLRequest, преобразую URL-адрес в http: и отправляю его на мой сервер.

Все работает для запросов HTTP GET. Проблема, с которой я сталкиваюсь, такова с почтовыми запросами. Похоже, что UIWebView неправильно кодирует данные формы в HTTPBody, если запрос использует мой пользовательский протокол.

Один обходной путь, поскольку я использую HTTPS для моих запросов сервера, заключается в том, что я регистрирую обработчик протокола для перехвата http: вместо myprotocol: и я могу конвертировать все вызовы в https: этот другой вопрос, здесь , указал мне на это решение:

Но мне интересно, есть ли какой-либо альтернативный и/или лучший способ достижения цели. то, что я хочу.

2 10

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] на основе других критериев (например, имя хоста).