.Net HttpWebRequest.GetResponse()は、httpステータスコード400(不正なリクエスト)が返されると例外を発生させます


205

私はサーバーからHTTP 400コードを取得したときに、サーバーがリクエストの何が問題であったかを(HTTP応答コンテンツのメッセージを使用して)通知する完全に合法的な状況にあります

ただし、ステータスコードが400の場合、.NET HttpWebRequestは例外を発生させます。

これをどのように処理しますか?私にとって、400は完全に合法であり、かなり役に立ちます。HTTPコンテンツにはいくつかの重要な情報が含まれていますが、例外が原因で私のパスから外れてしまいます。


6
同じことを経験していた。.NET Frameworkチームに提案を送信しました。それに投票してお気軽に:connect.microsoft.com/VisualStudio/feedback/details/575075/...
ジョナスStawski

回答:


344

「成功しないコードでスロー」をオフにする方法があったらいいのですが、WebExceptionをキャッチした場合は、少なくとも応答を使用できます。

using System;
using System.IO;
using System.Web;
using System.Net;

public class Test
{
    static void Main()
    {
        WebRequest request = WebRequest.Create("http://csharpindepth.com/asd");
        try
        {
            using (WebResponse response = request.GetResponse())
            {
                Console.WriteLine("Won't get here");
            }
        }
        catch (WebException e)
        {
            using (WebResponse response = e.Response)
            {
                HttpWebResponse httpResponse = (HttpWebResponse) response;
                Console.WriteLine("Error code: {0}", httpResponse.StatusCode);
                using (Stream data = response.GetResponseStream())
                using (var reader = new StreamReader(data))
                {
                    string text = reader.ReadToEnd();
                    Console.WriteLine(text);
                }
            }
        }
    }
}

「成功コードでなくても応答を受け取る」ビットを別のメソッドにカプセル化したい場合があります。(接続できなかった場合など、応答がない場合でもスローすることをお勧めします。)

エラー応答が大きい(これは珍しい)可能性がある場合は、微調整HttpWebRequest.DefaultMaximumErrorResponseLengthしてエラー全体を取得することをお勧めします。


5
WebExceptionに添付された応答でGetResponseStream()によって返されるストリームの内容は、サーバーによって実際に返される応答ではなく、単にステータスコード(たとえば、「Bad Request」)の名前です。この情報を取得する方法はありますか?
マークワッツ

@MarkWatts:それはすべきサーバから返されています、と私は見てきたどのような状況になっているものは何でも可能。これを特定の外部URLで再現できますか?新しい質問(この質問を参照)をして、何が起こっているのかを示すことをお勧めします。
Jon Skeet

これは、応答のコンテンツ長がゼロの場合にのみ行われることがわかります。HTTPステータスコードのテキストによる説明が追加されます。400は単なる「不正な要求」ですが、他のいくつかはより説明的です。
マークワッツ

誰かがこのクラスの素晴らしいラッパーを知っているなら、ここへのリンクをドロップしてください。System.Net.WebClientは、例外処理システムを呼び出すことによって同じように動作します。
John K

1
@AnkushJain:2XXの間は正常に戻ると思います。設定によっては、3XXでリダイレクト発生する可能性があります。間違っている可能性もありますが、他の場合は例外が発生することが予想されます。(4XXの場合、認証情報があればそれをまだ使用していない場合は、認証情報を適用する可能性があります。)
Jon Skeet

48

これはすでにかなり前に回答されていることは知っていますが、この質問に来た他の人々を助けるために拡張メソッドを作成しました。

コード:

public static class WebRequestExtensions
{
    public static WebResponse GetResponseWithoutException(this WebRequest request)
    {
        if (request == null)
        {
            throw new ArgumentNullException("request");
        }

        try
        {
            return request.GetResponse();
        }
        catch (WebException e)
        {
            if (e.Response == null)
            {
                throw;
            }

            return e.Response;
        }
    }
}

使用法:

var request = (HttpWebRequest)WebRequest.CreateHttp("http://invalidurl.com");

//... (initialize more fields)

using (var response = (HttpWebResponse)request.GetResponseWithoutException())
{
    Console.WriteLine("I got Http Status Code: {0}", response.StatusCode);
}

2
WebException.Responseすることができますnull。この場合は、再スローする必要があります。
Ian Kemp

@DavidP私は同意します。使用法は少しぎこちないですが、ほとんどの場合、HttpClient代わりに使用する必要があります。もっと構成しやすく、将来の道だと思います。
マシュー

リクエストのチェック== nullは本当に必要ですか?これは拡張メソッドなので、nullオブジェクトで使用しようとすると、拡張メソッドコードに到達する前にnull参照例外がスローされます。
kwill

@kwill拡張メソッドは単なる静的メソッドです。特に拡張メソッド構文で呼び出されない場合は、混乱を避けるために適切な入力検証を行う必要があります。また、これは、.NETチームによって行わ一貫したアプローチがある:github.com/dotnet/corefx/blob/...
マシュー

1
すみません、2つ目の質問を誤解しています。 ((WebRequest) null).GetResponseWithoutException()実際にはは発生しません。NullReferenceExceptionこれはと同等にコンパイルされるため、WebRequestExtensions.GetResponseWithoutException(null)結果としてが発生しないため、NullReferenceException入力の検証が必要になります。
マシュー

13

興味深いことに、HttpWebResponse.GetResponseStream()から取得するWebException.Responseは、サーバーから受信する応答ストリームとは異なります。私たちの環境では、オブジェクトを使用して400 HTTPステータスコードがクライアントに返されると、実際のサーバー応答が失われHttpWebRequest/HttpWebResponseます。これまでの説明から、に関連付けられた応答ストリームWebException's HttpWebResponseはクライアントで生成され、サーバーからの応答本文は含まれていません。悪い要求の理由をクライアントに返信したいので、非常にイライラします。


メソッドHEADを使用してこの例外を引き起こしていますが、GETを使用しても問題はありません。HEADメソッドの問題は正確に何ですか?
Mohammad Afrashteh

12

GoogleのOAuth2サービスに接続しようとすると、同様の問題が発生しました。

次のように、WebRequestを使用せずに手動でPOSTを記述しました。

TcpClient client = new TcpClient("accounts.google.com", 443);
Stream netStream = client.GetStream();
SslStream sslStream = new SslStream(netStream);
sslStream.AuthenticateAsClient("accounts.google.com");

{
    byte[] contentAsBytes = Encoding.ASCII.GetBytes(content.ToString());

    StringBuilder msg = new StringBuilder();
    msg.AppendLine("POST /o/oauth2/token HTTP/1.1");
    msg.AppendLine("Host: accounts.google.com");
    msg.AppendLine("Content-Type: application/x-www-form-urlencoded");
    msg.AppendLine("Content-Length: " + contentAsBytes.Length.ToString());
    msg.AppendLine("");
    Debug.WriteLine("Request");
    Debug.WriteLine(msg.ToString());
    Debug.WriteLine(content.ToString());

    byte[] headerAsBytes = Encoding.ASCII.GetBytes(msg.ToString());
    sslStream.Write(headerAsBytes);
    sslStream.Write(contentAsBytes);
}

Debug.WriteLine("Response");

StreamReader reader = new StreamReader(sslStream);
while (true)
{  // Print the response line by line to the debug stream for inspection.
    string line = reader.ReadLine();
    if (line == null) break;
    Debug.WriteLine(line);
}

応答ストリームに書き込まれる応答には、特定のエラーテキストが含まれています。

特に、私の問題は、URLエンコードされたデータの間にエンドラインを挿入していたことでした。私がそれらを取り出したとき、すべてがうまくいきました。同様の手法を使用してサービスに接続し、実際の応答エラーテキストを読み取ることができる場合があります。


2
HttpWebRequestはめちゃくちゃです。ソケットでも簡単です(エラーをユーザーから隠さないためです)。
Agent_L 2012

6

これを試してください(VBコードです:-):

Try

Catch exp As WebException
  Dim sResponse As String = New StreamReader(exp.Response.GetResponseStream()).ReadToEnd
End Try

3

拡張関数の非同期バージョン:

    public static async Task<WebResponse> GetResponseAsyncNoEx(this WebRequest request)
    {
        try
        {
            return await request.GetResponseAsync();
        }
        catch(WebException ex)
        {
            return ex.Response;
        }
    }

0

これは私のためにそれを解決しました:https
//gist.github.com/beccasaurus/929007/a8f820b153a1cfdee3d06a9c0a1d7ebfced8bb77

TL; DR:
問題:
localhostのリターンが期待コンテンツ、リモートIP変更「不正な要求」に400コンテンツ
ソリューション:
追加する<httpErrors existingResponse="PassThrough"></httpErrors>にはweb.config/configuration/system.webServer私のためにこれを解決しました。現在、すべてのサーバー(ローカルとリモート)は、返すIPアドレスやHTTPコードに関係なく、まったく同じコンテンツ(私が生成したもの)を返します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.