当返回HTTP状态代码400(错误请求)时,.Net HttpWebRequest.GetResponse()引发异常


205

我处于这样一种情况,当我从服务器获取HTTP 400代码时,这是服务器告诉我请求出了什么问题的完全合法方法(使用HTTP响应内容中的消息)

但是,.NET HttpWebRequest在状态代码为400时引发异常。

我该如何处理?对我来说400是完全合法的,并且非常有帮助。HTTP内容包含一些重要信息,但是异常使我无法正常工作。


6
我遇到了同样的事情。我向.NET Framework团队提出了建议。随意投赞成票:connect.microsoft.com/VisualStudio/feedback/details/575075/...
乔纳斯Stawski

Answers:


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
GetResponseStream()在附加到WebException的响应上返回的流的内容只是状态码的名称(例如,“错误请求”),而不是服务器实际返回的响应。有什么办法获取此信息?
马克·瓦茨

@MarkWatts:应该是服务器返回的任何值,并且在我见过的每种情况下都应该如此。您可以使用特定的外部URL复制吗?我建议您提出一个新问题(请参阅此问题)并说明正在发生的事情。
乔恩·斯基特

事实证明,只有在响应的内容长度为零时才这样做;它添加了HTTP状态代码的文字描述-400只是“错误请求”,但其他一些更具描述性。
马克·瓦茨

如果有人知道该类有一个不错的包装器,请在此处添加指向它的链接。System.Net.WebClient的行为与调用异常处理系统的行为相同。
约翰·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。如果是这种情况,您应该重新抛出。
伊恩·肯普

@DavidP我同意,用法有点过时,尽管对于大多数事情而言,您可能应该使用HttpClient它,它的可配置性要强得多,我相信这是未来的方式。
马修(马修)

是否真的需要检查请求== null?由于这是一个扩展方法,因此尝试在null对象上使用它应该在它到达扩展方法代码之前抛出null引用异常……对吗?
意志

@kwill扩展方法只是静态方法,应做适当的输入验证以避免混淆,尤其是当它们未使用扩展方法语法调用时。此外,这是由在.NET团队完成的一贯做法:github.com/dotnet/corefx/blob/...
马太福音

1
对不起,我误解了您的第二个问题。 ((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 '18

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

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代码如何,所有服务器(本地和远程)都返回完全相同的内容(由我生成)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.