Правильная обработка перенаправлений с NSURLConnection - PullRequest
25 голосов
/ 18 сентября 2009

Для этого я буду притворяться, что исходный URL-адрес http://host/form, а новый URL-адрес https://host/form. (Обратите внимание, что перед тем, как я отправлю это, оба URL-адреса будут безопасными. Однако небезопасный к безопасности кажется удобным перенаправлением для проверки этого.)

Я обращаюсь к веб-API с помощью NSURLConnection, который перенаправляет меня. По сути, я хочу взять все, что я только что отправил в http://hostaform и повторно отправить его в https://host/form. Я думал, что это будет поведение по умолчанию, но похоже, что тело теряется в перенаправлении.

Так что я думаю, что мне нужно обработать событие connection:willSendRequest:redirectResponse: делегата NSURLConnection и заново присоединить тело. Проблема в том, что это сообщение выглядит ужасно недокументированным. Единственная информация, которую я могу найти по этому методу, это Ссылка класса NSURLConnection , которая не очень полезна. Среди прочего, это включает в себя:

redirectResponse : ответ URL, вызвавший перенаправление. Может быть нулем в случаях, когда этот метод не отправляется в результате вовлечения делегата в обработку перенаправления.

Я не уверен, что это значит. В сочетании с начальным вызовом willSendRequest:, я думаю, это означает, что willSendRequest: отправляется даже для моего первоначального запроса, до ответа на перенаправление. Это правильно?

Итак, я добавил в свой делегат код для дополнительного сохранения тела и добавил обработчик willSendRequest::

- (NSURLRequest *)connection: (NSURLConnection *)inConnection
             willSendRequest: (NSURLRequest *)inRequest
            redirectResponse: (NSURLResponse *)inRedirectResponse;
{
    if (inRedirectResponse) {
        NSMutableURLRequest *r = [[inRequest mutableCopy] autorelease];
        [r setURL: [inRedirectResponse URL]];
        [r setHTTPBody: body];
        return r;
    } else {
        return inRequest;
    }
}

Это не работает. Но я даже не уверен, что это правильный подход. Это кажется чрезмерно хакерским для меня. Что мне делать? Это где-нибудь задокументировано? Пока что я не нашел ничего полезного в документации Apple или использовании Google.

(Это на iPhone, хотя, похоже, в этих классах нет большой разницы.)

Ответы [ 4 ]

38 голосов
/ 19 сентября 2009

В разделе 10.3.2 RFC 2616 есть примечание об этом поведении:

Примечание. При автоматическом перенаправлении запроса POST после получение кода состояния 301, некоторые существующие пользовательские агенты HTTP / 1.0 по ошибке изменит его на запрос GET.

Так что это поведение кажется нестандартным, но историческим. Этот запрос GET не является POST, и в нем будет отсутствовать полезная нагрузка.

Интересно, что это тоже в том же разделе:

Если код состояния 301 получен в ответ на запрос другого чем GET или HEAD, пользовательский агент НЕ ДОЛЖЕН автоматически перенаправлять запрос, если он не может быть подтвержден пользователем, так как это может изменить условия, на которых был выдан запрос.

Это довольно ясно и, кажется, указывает на то, что мы не можем это исправить, но я думаю, что игнорирование этого для целей наших собственных клиентов веб-сервисов для сервисов, которые мы выбираем (или контролируем), вероятно, является наименее плохой альтернативой.

Так, как мы решаем это?

Вместо willSendResponse: в исходном вопросе я использую это:

- (NSURLRequest *)connection: (NSURLConnection *)connection
             willSendRequest: (NSURLRequest *)request
            redirectResponse: (NSURLResponse *)redirectResponse;
{
    if (redirectResponse) {
        // we don't use the new request built for us, except for the URL
        NSURL *newURL = [request URL];
        // Previously, store the original request in _originalRequest.
        // We rely on that here!
        NSMutableURLRequest *newRequest = [_originalRequest mutableCopy];
        [newRequest setURL: newURL];
        return newRequest;
    } else {
        return request;
    }
}

Идея заключается в том, что вместо клонирования нового запроса и попытки сформировать его так же, как отправляет мне Cocoa Touch, я создаю клон исходного запроса и изменяю только URL-адрес в соответствии с запросом, отправленным мне Cocoa Touch. , Этот исходный запрос по-прежнему POST с прикрепленной полезной нагрузкой.

Если вы управляете сервером, стоит прочитать RFC 2616, раздел 10.3 , чтобы увидеть, есть ли лучший код, который вы можете использовать (при проверке, конечно, iOS обрабатывает лучший код как это должно).

Вы также можете сделать изменяемую копию перенаправленного запроса и заменить его метод HTTP на метод HTTP исходного запроса. Тот же самый общий принцип, хотя это будет способствовать сохранению вещей из нового запроса, а не из старого. В некоторых случаях это может работать лучше, но я еще не проверял это.

13 голосов
/ 03 августа 2010

Вы должны проверить код состояния ответа HTTP, отправленный сервером, чтобы определить, следует ли отправлять GET или повторять POST. Для 303 (или 302) отправьте запрос GET. Для 307 повторите POST.

4 голосов
/ 06 марта 2011

У меня была такая же проблема с перенаправлением. Спасибо AJSoaks! Я попытался, как он предложил, и проблема решена.

Итак, я пытался опубликовать имя пользователя и пароль с помощью метода POST, и я увидел, что сервер перенаправил мой запрос. Как говорит AJSoaks, в случае ошибки 302 вы должны повторить запрос, но на этот раз, используя метод GET вместо предыдущего POST.

... в какой-то момент у вас есть следующие строки: ... он может быть внутри, если ваш метод IBAction (нажата кнопка) или где вы хотите ...

NSMutableString *postString = [[NSMutableString alloc] init];

[postString appendString:@"username=YourUsername&password=YourPassword"];

    //the original URL (https means that it supports SSL protocol)
    //it doesn't change anything, don't worry about it
NSURL *URL = [NSURL URLWithString:@"https://loginWebpageURL"];

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];

[request setHTTPMethod:@"POST"];    
[request setValue:[NSString stringWithFormat:@"%d", [postString length]] forHTTPHeaderField:@"Content-length"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-type"];
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];

[NSURLConnection connectionWithRequest:request delegate:self];

[postString release];
[request release];

Чем вы должны также реализовать метод делегата NSURLConnection для перенаправления со следующей подписью:

- (NSURLRequest *)connection:(NSURLConnection *)connection
            willSendRequest:(NSURLRequest *)request
           redirectResponse:(NSURLResponse *)redirectResponse

внутри этого метода, в случае, если у вас есть ошибка СЕРВЕРА 302 или 303, вы должны реализовать что-то похожее на код ниже, просто скопируйте код, который вы видите, и замените его новым URL (перенаправленным). Новый URL вы можете увидеть в браузере или, если хотите, он будет очень полезен, и в будущем, проверяя его с помощью Firebug (плагин Firefox) или Safari WEB INSPECTOR. Если вы используете Firebug всю информацию, которую вы можете найти в разделе «Сеть»:

if (redirectResponse) {

    NSLog(@"REDIRECT");
    NSMutableURLRequest *requestTmp = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://areaclienti.tre.it/selfcare/areaclienti133/4552_infoCosti_ITA_HTML.xsl"]];

    return [requestTmp autorelease];
}

//return original request in case thay there is no redirecting...
else return request;
0 голосов
/ 01 апреля 2014

NSURLConnection не добавляет заголовки originalRequest в перенаправленный запрос в "willSendRequest: (NSURLRequest *) inRequest".

Вы можете обойти эту проблему, добавив «originalRequest.headers» в перенаправленный запрос.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...