NSURLConnectionでリダイレクトを正しく処理する 質問する

NSURLConnectionでリダイレクトを正しく処理する 質問する

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

リダイレクトされる を使用して Web API にアクセスしていますNSURLConnection。基本的には、 に送信したすべての内容を取得しhttp://hostaformて に再送信しますhttps://host/form。これがデフォルトの動作だと思っていましたが、リダイレクトで本文が失われているようです。

connection:willSendRequest:redirectResponse:だから、デリゲートのイベントを処理して、ボディを再アタッチする必要があると思いますNSURLConnection。問題は、このメッセージはひどく文書化されていないようです。このメソッドについて私が見つけることができる唯一の情報は次のとおりです。NSURLConnection クラスリファレンスあまり役に立ちません。 他にも次のような内容が含まれています。

リダイレクトレスポンス : リダイレクトの原因となった URL 応答。リダイレクト処理にデリゲートが関与したためにこのメソッドが送信されない場合は nil になることがあります。

これが何を意味するのかよく分かりません。最初の呼び出しと組み合わせると、リダイレクト応答の前に、最初のリクエストに対しても送信されていることになるwillSendRequest:と思います。これで正しいでしょうか?willSendRequest:

そこで、デリゲートにコードを追加して、本文をさらに 1 回保持し、次の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 の場合ですが、これらのクラスに大きな違いはないようです。)

ベストアンサー1

メモがありますRFC 2616 のセクション 10.3.2この行動について:

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

したがって、この動作は非標準ですが、歴史的であると思われます。そのGETリクエストは ではなくPOST、ペイロードが欠落します。

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

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

これはかなり明白で、これを修正できないことを示しているようですが、私たちが選択する (または制御する) サービス用の独自の 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 から送信されたものと同じ形にしようとするのではなく、元のリクエストの複製を作成し、URL だけを変更して Cocoa Touch から送信されたリクエストと一致させるというものです。元のリクエストには、POSTペイロードが添付されたままです。

サーバーを管理している場合は読む価値がありますRFC 2616、セクション 10.3全体をチェックして、より優れたコードがあるかどうかを確認します (もちろん、iOS がより優れたコードを適切に処理することを確認します)。

リダイレクトされたリクエストの可変コピーを作成し、その HTTP メソッドを元のリクエストの HTTP メソッドに置き換えることもできます。同じ基本原則ですが、古いリクエストではなく新しいリクエストからのものを保持することを優先します。状況によっては、これがより効果的かもしれませんが、まだテストしていません。

おすすめ記事