NSURLConnectionでリダイレクトを正しく処理する
-
22-07-2019 - |
質問
このため、元のURLは http:// host / form
であり、新しいURLは https:// host / form コード>。 (これを出荷する前に、両方のURLがセキュアになることに注意してください。ただし、非セキュアからセキュアへは、これをテストするのに便利なリダイレクトのようです。)
私はリダイレクトする NSURLConnection
を使用してWeb APIにアクセスしています。基本的に、 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にありますが、これらのクラスに大きな違いはないようです。)
解決
RFC 2616のセクション10.3.2 に注意事項があります。この動作:
注:POST要求を自動的にリダイレクトする場合 301ステータスコード、いくつかの既存のHTTP / 1.0ユーザーエージェントの受信 誤ってGETリクエストに変更します。
したがって、この動作は標準的ではないようですが、歴史的なものです。その GET
リクエストは POST
ではなく、ペイロードが欠落しています。
興味深いことに、これも同じセクションにあります:
301ステータスコードが他のリクエストへの応答として受信された場合 GETまたはHEADよりも、ユーザーエージェントは自動的に ユーザーが確認できない限り、リクエストする リクエストが発行された条件を変更します。
これは非常に明確であり、これを修正できないことを示しているようですが、選択(または制御)するサービスの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から送られてきたものと同じ形にしようとする代わりに、元のリクエストのクローンを作成し、Cocoa Touchから送られたリクエストに合わせてURLのみを変更することです。元のリクエストは、ペイロードが添付された POST
のままです。
サーバーを制御する場合、 RFC 2616、セクション10.3 を読む価値があります。使用できるより優れたコードがあるかどうかを全体的に確認します(もちろん、iOSが適切なコードを処理することを確認しながら)。
リダイレクトされたリクエストの可変コピーを作成し、そのHTTPメソッドを元のリクエストのHTTPメソッドに置き換えることもできます。同じ一般的な原則ですが、それは古いリクエストよりも新しいリクエストから物を守ることを支持するでしょう。状況によってはうまくいくかもしれませんが、これはまだテストしていません。
他のヒント
サーバーから送信されたHTTP応答ステータスコードを確認して、GETを送信するかPOSTを繰り返すかを決定する必要があります。 303(または302)の場合、GET要求を送信します。 307については、POSTを繰り返します。
iにはリダイレクトでも同じ問題がありました。 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 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;
NSURLConnectionは、" willSendRequest:(NSURLRequest *)inRequest"のリダイレクトされたリクエストにoriginalRequestヘッダーを追加しません。
" originalRequest.headers"を追加することにより、この問題を回避できます。リダイレクトされたリクエストに。