Подтвердить что ты не робот

Управление перенаправлением с помощью NSURLConnection

В целях этого я буду притворяться, что исходный 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, хотя, похоже, в этих классах нет большой разницы.)

4b9b3361

Ответ 1

Здесь есть примечание в разделе 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 исходного запроса. Тот же общий принцип, хотя это будет способствовать сохранению вещей из нового запроса, а не старого. В некоторых случаях это может работать лучше, но я еще не тестировал это.

Ответ 2

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

Ответ 3

У меня была та же проблема с перенаправлением. Благодаря 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

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

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;

Ответ 4

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

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