C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[11]: WebException.Reponseの解放は?


(過去ログ 79 を表示中)

[トピック内 12 記事 (1 - 12 表示)]  << 0 >>

■46882 / inTopicNo.1)  WebException.Reponseの解放は?
  
□投稿者/ れい (870回)-(2010/02/13(Sat) 05:10:26)

分類:[.NET 全般] 

れいです。
疑問があります。
お知恵を拝借いたしたく。

WebRequest.GetResonseなどを使った際に例外が発生するとWebExceptionが投げられます。
WebException.Reponseを見てプロトコルエラーだとか認証できなかったとか調べるわけですが、
このResponseはIDisposableを実装しています。

で、このResponseのCloseを呼ぶ必要があるのかどうか、呼ぶとしたらいつ、どこで呼ぶべきなのかがわかりません。

いつもなら「例外はめったにおきないから廃品回収に任せたらいいんじゃない?」とするのですが、
ファイルアクセスなどと違い、ネットワークアクセスの場合は例外が頻繁に起きるので心配です。
(クローラを作っていたら404レスポンスも当たり前です。)

WebRequest/Responseを自分で実装しているのですが、
ReponseをCloseできないといつまでもリソースを使ってしまうし、
かといってCloseしてからWebExceptionを投げると、GetReponseStreamが使えません。

例外を捕まえる時に、いちいちWebExceptionかどうか、WebException.Reponseがあるかどうかを確認しCloseすればよいのですが、
ライブラリを作る場合は困ります。

せっかくの構造化例外処理、せっかくのIDisposableが活きていない気がします。

より一般に。
ExceptionにIDisposableなメンバを入れたい場合に、どう入れたらいいのでしょう?
ExceptionにIDisposableなメンバが入っている場合に、どう廃棄したらいいのでしょう?

ExceptionがIDisposableを実装していて、
tryを出るとDisposeされる仕様だとよかったのに。

引用返信 編集キー/
■46907 / inTopicNo.2)  Re[1]: WebException.Reponseの解放は?
□投稿者/ こあら (73回)-(2010/02/13(Sat) 20:48:32)
No46882 (れい さん) に返信
> で、このResponseのCloseを呼ぶ必要があるのかどうか、
> 呼ぶとしたらいつ、どこで呼ぶべきなのかがわかりません。
> ExceptionがIDisposableを実装していて、
> tryを出るとDisposeされる仕様だとよかったのに。

WebExceptionのデストラクタでWebException.ResponseをDisposeするのでは、
結局GC任せになってしまう。ということ・・・です・・・よね?


> ResponseをCloseできないといつまでもリソースを使ってしまうし、
> かといってCloseしてからWebExceptionを投げると、GetResponseStreamが使えません。

404などGetResponseStreamを使う必要のない例外を、
自作の(Responseのない軽い?)Exceptionに差し替えるのは対症療法にならないでしょうか?

引用返信 編集キー/
■46923 / inTopicNo.3)  Re[2]: WebException.Reponseの解放は?
□投稿者/ れい (871回)-(2010/02/14(Sun) 17:49:38)
No46907 (こあら さん) に返信
> ■No46882 (れい さん) に返信
>>で、このResponseのCloseを呼ぶ必要があるのかどうか、
>>呼ぶとしたらいつ、どこで呼ぶべきなのかがわかりません。
>>ExceptionがIDisposableを実装していて、
>>tryを出るとDisposeされる仕様だとよかったのに。
>
> WebExceptionのデストラクタでWebException.ResponseをDisposeするのでは、
> 結局GC任せになってしまう。ということ・・・です・・・よね?

はい。
WebExceptionは明示的にデストラクタを実装してないので
Responseはかなり長いこと解放されないのです。

せめてIDisposableを実装していれば…

>>ResponseをCloseできないといつまでもリソースを使ってしまうし、
>>かといってCloseしてからWebExceptionを投げると、GetResponseStreamが使えません。
>
> 404などGetResponseStreamを使う必要のない例外を、
> 自作の(Responseのない軽い?)Exceptionに差し替えるのは対症療法にならないでしょうか?

差し替えるとResponse.GetReponseStreamが使えないのです。

たとえば。

HTTPは非常に広く使われているので、
「エラー時のメッセージボディがすごく長い」
「エラー時のメッセージボディにも意味がある」
ような実装も多々あります。

そういったサーバーに接続して、エラーが返ってきた場合、
用途によってはメッセージボディが必要になります。
かといって、用途によってはメッセージボディは必要ないので、
メッセージボディをあらかじめ全部読んでバッファに入れるのは無駄です。
また、用途によっては接続を切断する必要もあります。

なので、Response.GetResponseStreamは必須です。

同一のHTTPサーバーに対する接続は.Netの規定では2本までです。(ServicePointManager.DefaultConnectionLimit)
2回リクエストが失敗すると、GCがWebException.Responseを廃棄してくれるまで、
接続ができなくなります。

IISやApacheは「エラー時に接続を切る」というRFC非準拠の仕様になっているので
大抵の場合は問題にならないのでしょうが、
RFC準拠なサーバーはエラー時にも接続を切らず、再利用しようとしますので、
接続できなくなります。

それに気づけば、毎回WebExceptionを捕まえてResponseを廃棄すればいいだけなのですが、
WebRequestを使う人がすぐにそれに気づけるとは思えません。

というか。
ついこの間まで私も気づいておらず、廃棄していませんでした。

私はいつも例外が起きてほしくないUI側の一番上でTry/Catchで囲んで済ませるのですが、
WebExceptionだけ別にCatchする必要があるわけです。

なら、ほかにもIDisposableを実装したメンバを持つExceptionもあるはずなので、
それも別にキャッチしないといけないことになります。

なんか変だな、と思うのですが。

引用返信 編集キー/
■47006 / inTopicNo.4)  Re[3]: WebException.Reponseの解放は?
□投稿者/ こあら (75回)-(2010/02/16(Tue) 00:28:24)
> なんか変だな、と思うのですが。

そうですね。私も変だと思います。というか、フレームワークの設計ミスでは・・・


> try
> return response
> finally
> if response.status = error then
> throw new WebException(response)
> end if
> end try

みたいに、WebRequest.GetResponseの戻り値とWebException.Responseが同じインスタンスを指す実装って
できないんでしたっけ?

引用返信 編集キー/
■47007 / inTopicNo.5)  Re[4]: WebException.Reponseの解放は?
□投稿者/ れい (875回)-(2010/02/16(Tue) 00:43:49)
2010/02/16(Tue) 01:55:21 編集(投稿者)
No47006 (こあら さん) に返信
>>なんか変だな、と思うのですが。
> 
> そうですね。私も変だと思います。というか、フレームワークの設計ミスでは・・・

お。
同志が。

> みたいに、WebRequest.GetResponseの戻り値とWebException.Responseが同じインスタンスを指す実装って
> できないんでしたっけ?

できるんですが…。

InnerMethod( arg, arg, ... ) {
  try {
    response = request.GetReponse;
    return response;
  } finally {
    if (request.status = error) throw new webexception(response)
  }
}

returnされたreponseを使う側では普通はUsingを使いますよね。

OuterMethod {
  try
    Using (response = SomeMethodReturnsResponse( args, args )) {
      '正常系
    }
  } catch ex as WebException {
    if ( ex.Response.Status == XX ) {  #ここでObjectDisposedException
      MessageBos("some error occurred.")
    }
    using ( stream = ex.Response.GetReponseStream ) { #ここでObjectDisposedExceptionやIOException
      ReadErrorMessage(stream)
    }
  }
}

という感じで、
普通に使うことができなくなってしまいます。

Usingを使わずにFinallyで囲んで、WebExceptionのキャッチを他の例外のキャッチと毎回確実に別にすればいいのですが…。

やってられない。

引用返信 編集キー/
■47042 / inTopicNo.6)  Re[5]: WebException.Reponseの解放は?
□投稿者/ こあら (77回)-(2010/02/17(Wed) 01:05:20)
ここまで来てやっと、質問の最初に追いつけたかもしれませんw

> ExceptionがIDisposableを実装していて、
> tryを出るとDisposeされる仕様だとよかったのに。

なるほど。
引用返信 編集キー/
■47045 / inTopicNo.7)  Re[6]: WebException.Reponseの解放は?
□投稿者/ 渋木宏明(ひどり) (1280回)-(2010/02/17(Wed) 08:31:41)
渋木宏明(ひどり) さんの Web サイト
>>ExceptionがIDisposableを実装していて、
>>tryを出るとDisposeされる仕様だとよかったのに。

それはそれで、再スローと相性が良くないような。
WebException が IDisposable を実装する、とかも変かなぁ。
引用返信 編集キー/
■47048 / inTopicNo.8)  Re[7]: WebException.Reponseの解放は?
□投稿者/ ななし (15回)-(2010/02/17(Wed) 11:15:48)
こんにちは。はずしてたらスルーしてください。

れいさんの、

> WebRequest/Responseを自分で実装しているのですが、

とか、

> ライブラリを作る場合は困ります。

のところがどんな状況か想像できていないのと、

> より一般に。
> ExceptionにIDisposableなメンバを入れたい場合に、どう入れたらいいのでしょう?
> ExceptionにIDisposableなメンバが入っている場合に、どう廃棄したらいいのでしょう?

の話でもないのですが、

static class WebRequestExtensions
{
    public static WebResponse GetResponse(
        this WebRequest webRequest,
        out WebException webExceptionWithoutRespose)
    {
        try
        {
            webExceptionWithoutRespose = null;
            return webRequest.GetResponse();
        }
        catch (WebException e)
        {
            if (e.Response != null)
            {
                webExceptionWithoutRespose =
                    new WebException(e.Message, e.Status);
                return e.Response;
            }
            throw;
        }
    }
}

としておいて、

MyMethod()
{
    var request = WebRequest.Create("http://www.google.co.jp/");

    WebException webExceptionWithoutRespose;
    using (var response = request.GetResponse(out webExceptionWithoutRespose))
    {
        using (var stream = response.GetResponseStream())
        {
            using (var reader = new StreamReader(stream))
            {
                richTextBox1.ForeColor =
                    webExceptionWithoutRespose == null
                    ? Color.Blue : Color.Red;
                richTextBox1.Text = reader.ReadToEnd();
            }
        }
    }
}

のように使うのはどうですか?

引用返信 編集キー/
■47074 / inTopicNo.9)  Re[8]: WebException.Reponseの解放は?
□投稿者/ れい (878回)-(2010/02/18(Thu) 01:13:52)
No47045 (渋木宏明(ひどり) さん) に返信
> >>ExceptionがIDisposableを実装していて、
> >>tryを出るとDisposeされる仕様だとよかったのに。
> 
> それはそれで、再スローと相性が良くないような。

そうなんですよね。
再スローされるExceptionなのにIDisposableがメンバって、
失敗作な雰囲気が感じられます。

■No47048 (ななし さん) に返信
> こんにちは。はずしてたらスルーしてください。
>略
> のように使うのはどうですか?

外していません。
こういった使い方をすればusingできちんとReponseを廃棄することができますね。

でもこれだと異常系がusingの中に入ってしまっています。
構造化されていません。
「ファイルAとファイルBの内容を連結してCに書く」という単純な作業の場合、

try {
  using (var responseA = requestA.GetResponse(out webExceptionWithoutResposeA)) {
    using (var streamA = responseA.GetResponseStream()) {
      if ( webExceptionWithoutResposeA != null ) {
        using (var responseB = requestB.GetResponse(out webExceptionWithoutResposeB)) {
          using (var streamB = responseB.GetResponseStream()) {
            if ( webExceptionWithoutResposeA != null ) {
              //ここに正常系の処理
            } else {
              //ここに異常系の処理
              //異常系のstreamBはここでしか使えないので、その処理が必要。
            }
          }
        }
      } else {
        //ここにも異常系の処理
        //異常系のstreamAはここでしか使えないので、その処理が必要。
      }
    }
} catch {
  //ここにも異常系の処理
}

となって、異常系が分散していて見にくくなります。

上記例だと一つのMethodの中なのでまだ耐えられますが、
異常系を処理したいのが違う階層のメソッドだとするとかなり大変になります。

なんかひどく醜いので
生産性だとか意思疎通だとか教育だとか
そういった大人な経験のある方々はどうしてるのかと。

これを他人に見せたら、
「なんで例外をキャッチしてパラメーターとして返してるの?それってデザインに反してない?」
とか
「なんで正常系と異常系が同じ構造(using)内で条件分岐してるの?構造化例外処理しないの?」
とか、
なりそうなわけです。

「処理できない例外は上に投げていい」というのが構造化例外処理なのに、WebExceptionだけは「処理できなきゃダメ」というのが問題だと思うのです。
だとすると、ななしさんのように、メソッドの返り値にして毎回確認するべきで、例外を投げるという実装はおかしいと思うのですが。
WebExceptionを投げる実装にはどんな利点があるのかわかりません。

もうひとつ問題なのが、こういった処理がIDisposableなメンバをもつExceptionでいつも起こるのに、
発生しうる例外をすべて列挙するのが無理なことです。
ドキュメントを全部漁って、そういうExceptionのリストでも作ればいいんですが、面倒でまだできていません。


「処理しなきゃいけない例外」がいつどこで起きるかわからないなんて!

いつどこで起きるかわからない「絶対に処理しなきゃいけない仕事」だって、
風邪ひきましたって言って上司に任せることができるのに…


考え始めてもう一週間くらいになるので、とりあえず解決したことにします。
WebRequest/Responseは構造化例外処理できない。
ななしさんのように使うべし、ということで。

解決としますが、なにか知っていること、考えたこと、アイデアなどがあったら教えてください。
よろしくお願いします。

引用返信 編集キー/
■47076 / inTopicNo.10)  Re[9]: WebException.Reponseの解放は?
□投稿者/ こあら (79回)-(2010/02/18(Thu) 02:40:15)
GetしたらfinallyでCloseしろパターン?
WebRequestにCloseResponseメソッドを持たせるのはダメでしょうか?


try
    Using (response = WebRequest.GetResponse()) {
      '正常系
    }
} catch ex as WebException {
    if ( ex.Response.Status == XX ) {
        MessageBos("some error occurred.")
    }
    using ( stream = ex.Response.GetReponseStream ) {
        ReadErrorMessage(stream)
    }
} finally {
    WebRequest.CloseResponse()   <-- if (this.Response) Response.Close のようなメソッド
}

引用返信 編集キー/
■47089 / inTopicNo.11)  Re[10]: WebException.Reponseの解放は?
□投稿者/ ななし (16回)-(2010/02/18(Thu) 11:21:14)
2010/02/18(Thu) 11:21:41 編集(投稿者)
こんにちは。

■No47074 (れい さん) に返信
> 外していません。
> こういった使い方をすればusingできちんとReponseを廃棄することができますね。

よかったです。
異常時にも stream を返しちゃえば、忘れずに破棄しやすくできる思いました。

> でもこれだと異常系がusingの中に入ってしまっています。
> 構造化されていません。
> 「ファイルAとファイルBの内容を連結してCに書く」という単純な作業の場合、
> ・・・
> となって、異常系が分散していて見にくくなります。

れいさんのコード、少し見やすくしてみました。↓

try
{
    var requestA = WebRequest.Create("http://www.google.co.jp/");

    WebException webExceptionWithoutRespose;
    using (var responseA = requestA.GetResponse(out webExceptionWithoutRespose))
    using (var streamA = responseA.GetResponseStream())
    {
        if (webExceptionWithoutRespose != null)
        {
            webExceptionWithoutRespose.Data.Add(
                "エラー内容",
                "streamA から取得したレスポンス");
            throw webExceptionWithoutRespose;
        }

        var requestB = WebRequest.Create("http://www.google.co.jp/");

        using (var responseB = requestB.GetResponse(out webExceptionWithoutRespose))
        using (var streamB = responseB.GetResponseStream())
        {
            if (webExceptionWithoutRespose != null)
            {
                webExceptionWithoutRespose.Data.Add(
                    "エラー内容",
                    "streamB から取得したレスポンス");
                throw webExceptionWithoutRespose;
            }

            // A と B の内容を連結して C に書くなどの正常系の処理
        }
    }
}
catch
{
    // ここで異常系の処理
}

これだと正常系と異常系がそれなりに分けられてると思いますし、「異常系を処理したいのが違う階層のメソッド」でも大丈夫じゃないですか?
昨日、Data.Add のあたりも拡張メソッドの中に入れちゃおうかと思ったりもしてたんですが、いらない人もいるということなのでやめてました。
ところで、Stream の読み込み中に Response 付きの WebException が発生したりなんてことはないですよね? 昨日の返信後、そこを心配してました。

> WebExceptionを投げる実装にはどんな利点があるのかわかりません。

確かに。

> もうひとつ問題なのが、こういった処理がIDisposableなメンバをもつExceptionでいつも起こるのに、
> 発生しうる例外をすべて列挙するのが無理なことです。
> ドキュメントを全部漁って、そういうExceptionのリストでも作ればいいんですが、面倒でまだできていません。

Exception から派生しているクラスのプロパティもしくはメソッドの戻り値に、IDisposable を実装しているものって、他にあるんですかね。
プログラムで調べればすぐわかりそうに思いますけど、私だとコード書くのに時間かかっちゃいます。


■No47076 (こあら さん) に返信
> GetしたらfinallyでCloseしろパターン?
> WebRequestにCloseResponseメソッドを持たせるのはダメでしょうか?

この具体的な実装を考えてみたんですが、難しそうに思いました。

引用返信 編集キー/
■47104 / inTopicNo.12)  Re[11]: WebException.Reponseの解放は?
□投稿者/ 渋木宏明(ひどり) (1281回)-(2010/02/18(Thu) 13:44:18)
渋木宏明(ひどり) さんの Web サイト
>>もうひとつ問題なのが、こういった処理がIDisposableなメンバをもつExceptionでいつも起こるのに、
>>発生しうる例外をすべて列挙するのが無理なことです。
>>ドキュメントを全部漁って、そういうExceptionのリストでも作ればいいんですが、面倒でまだできていません。
>
> Exception から派生しているクラスのプロパティもしくはメソッドの戻り値に、IDisposable を実装しているものって、他にあるんですかね。

標準ライブラリにはもうないとしても、自作することは可能なわけで、他人の作ったライブラリやフレームワークをつかっていると、どこかしらから混入してしまう可能性が…
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -