web-dev-qa-db-ja.com

NSURLConnectionを使用してリダイレクトを正しく処理する

このために、元のURLは_http://Host/form_であり、新しいURLは_https://Host/form_であると仮定します。 (これを出荷する前に、両方のURLが安全になることに注意してください。ただし、非セキュアからセキュアへのリダイレクトは、これをテストするための便利なリダイレクトのようです。)

リダイレクトするNSURLConnectionを使用してWebAPIにアクセスしています。基本的には、_http://hostaform_に送信したすべてのものを取得して、_https://Host/form_に再送信したいと思います。これがデフォルトの動作になると思いましたが、リダイレクトで本文が失われているようです。

したがって、NSURLConnectionのデリゲートの_connection:willSendRequest:redirectResponse:_イベントを処理し、本体を再アタッチする必要があると思います。問題は、このメッセージがひどく文書化されていないように見えることです。このメソッドで私が見つけることができる唯一の情報はNSURLConnectionクラスリファレンスであり、これはあまり役に立ちません。とりわけ、これが含まれます:

redirectResponse:リダイレクトの原因となったURL応答。デリゲートをリダイレクト処理に関与させた結果、このメソッドが送信されない場合は、nilになる可能性があります。

これが何を意味するのかわかりません。最初の_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にありますが、これらのクラスには大きな違いはないようです。)

25
Steven Fisher

RFC 2616のセクション10.3.2 にこの動作に関する注記があります。

注:301ステータスコードを受信した後にPOSTリクエストを自動的にリダイレクトすると、既存のHTTP /1.0ユーザーエージェントの中には誤ってGETリクエストに変更するものがあります。

したがって、この動作は非標準的ですが、歴史的なもののようです。そのGETリクエストはPOSTではなく、ペイロードが欠落しています。

興味深いことに、これも同じセクションにあります。

GETまたはHEAD以外の要求に応答して301ステータスコードを受信した場合、ユーザーが確認できない限り、ユーザーエージェントは要求を自動的にリダイレクトしてはなりません。これにより、要求が発行された条件が変わる可能性があります。

これは非常に明確であり、これを修正できないことを示しているようですが、私たちが選択(または制御)するサービスの独自のWebサービスクライアントの目的でこれを無視することは、おそらく最も悪い選択肢ではないと思います。

では、どうすればこれを解決できますか?

元の質問の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から送信されたものと同じように形作るのではなく、元のリクエストのクローンを作成し、CocoaTouchから送信されたリクエストに一致するようにURLだけを変更するというものです。その元のリクエストは、ペイロードが添付されたPOSTのままです。

サーバーを制御している場合は、 RFC 2616、セクション10. 全体を読んで、使用できるより良いコードがあるかどうかを確認する価値があります(もちろん、iOSがより良いコードを適切に処理することを確認します) )。

リダイレクトされたリクエストの可変コピーを作成し、そのHTTPメソッドを元のリクエストのHTTPメソッドに置き換えることもできます。同じ一般原則ですが、古いものよりも新しい要求から物事を遠ざける方が有利です。状況によってはうまくいくかもしれませんが、私はまだこれをテストしていません。

38
Steven Fisher

サーバーから送信されたHTTP応答ステータスコードをチェックして、GETを送信するかPOSTを繰り返すかを決定する必要があります。 303(または302)の場合は、GET要求を送信します。 307については、POSTを繰り返します。

13
AJSoaks

リダイレクトに関しても同じ問題がありました。 AJSoaksに感謝します!彼の提案どおりに試してみたところ、問題は解決しました。

だから、私はPOSTメソッドを介してユーザー名とパスワードを投稿しようとしました、そして私はそのサーバーが私のリクエストをリダイレクトするのを見ました。AJSoaksが言うように、302エラーがある場合はリクエストを繰り返す必要がありますただし、今回は以前のPOSTの代わりにGETメソッドを使用します。

...ある時点で、次の行があります。..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のエラー302または303が発生した場合は、次のコードのようなものを実装する必要があります。表示されたコードをコピーして、新しいURL(リダイレクト)に置き換えてください。ブラウザに表示される新しいURL、または必要に応じて、将来的にはFirebug(Firefoxプラグイン)またはSafari WEBINSPECTORで確認することもできます。 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;
4
klusinyan

NSURLConnectionは、「willSendRequest:(NSURLRequest *)inRequest」のリダイレクトされたリクエストにoriginalRequestヘッダーを追加しません。

リダイレクトされたリクエストに「originalRequest.headers」を追加することで、この問題を回避できます。

0
user2088250