Windows Phone の WebException のハンドリング

Windows Phone の WebRequest で リクエストを送った際にネットワークやサーバ由来のエラーがあると WebException 例外が発生するんですが、この例外のStatusプロパティやResponseプロパティにはクセがあって以下のような現象が発生します。

  • 存在するサーバの、存在しないリソースをリクエストすると、例外のStatusはWebExceptionStatus.UnknownErrorだったり WebExceptionStatus.ProtocolError だったりする。例外のResponse にはサーバからの404レスポンスが正しく格納されている。
  • 接続できないサーバの、存在しないリソースをリクエストすると、例外のStatusはWebExceptionStatus.UnknownErrorになる。例外のResponse には適当にでっち上げられた404レスポンスが格納されている。

サーバに接続できてもいないのに適当にレスポンスをでっち上げるという実装ポリシーも関心しませんが、そのレスポンスのステータスが404だというのも関心しません。接続できないという現象は別に永続的なものではないはずです。

実際、このレスポンスをそのまま信用すると、呼び出し側としては接続のリトライを早急に諦めるか、サーバが404を返しているにも関わらずリトライを続けることになってしまいます。


例外のResponseに含まれる偽物のResponseはHeadersがカラになっています。それを確認することで、サーバから返ってきた本物の404とWebRequestがでっち上げたニセの404を区別することができます。

コード例はこんな感じです。

HttpWebResponse res;
try {
	res = req.EndGetResponse( async_result ) as HttpWebResponse;
	if( !req.HaveResponse ) {
		result.last_status = 0;
		result.last_error = "No Response.";
		log.e( "{0} url={1}",result.last_error,url );
		continue;
	}
}catch(WebException ex){
	if( ex.Response != null && ex.Response.Headers.Count > 0 
	&& (ex.Status == WebExceptionStatus.ProtocolError || ex.Status == WebExceptionStatus.UnknownError )
	){
		res = ex.Response as HttpWebResponse;
	}else{
		result.last_status = 0;
		result.last_error = String.Format( "WebException {0} {1}",ex.Status,ex.Message );
		log.e( "{0} url={1}",result.last_error,url );
		continue;
	}
}catch(Exception ex){
	log.ex( ex,"EndGetResponse url={0}",url );
	result.last_error = TextUtil.format_ex( ex );
	result.last_status = 0;
	continue;
}
result.last_status = parseStatusCode( res.StatusCode );
result.last_error = res.StatusDescription;
result.headers = res.Headers;