我遇到了这样的情况:当服务器返回 HTTP 400 状态码时,它可以通过 HTTP 响应内容中的消息合法地告诉我请求存在问题。
然而,.NET HttpWebRequest 在状态码为 400 时会抛出异常。
我该如何处理这个问题呢?对我来说,400 是完全合法且非常有帮助的。HTTP 内容包含一些重要信息,但异常却使我偏离了正确的路径。
我遇到了这样的情况:当服务器返回 HTTP 400 状态码时,它可以通过 HTTP 响应内容中的消息合法地告诉我请求存在问题。
然而,.NET HttpWebRequest 在状态码为 400 时会抛出异常。
我该如何处理这个问题呢?对我来说,400 是完全合法且非常有帮助的。HTTP 内容包含一些重要信息,但异常却使我偏离了正确的路径。
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)
{
// TODO: Handle response being null
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
以确保获取完整的错误信息。我知道这个问题早已有答案,但我写了一个扩展方法,希望能帮助其他来到这个问题的人。
代码:
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);
}
WebException.Response
可能为 null
。如果是这种情况,您应该重新抛出异常。 - Ian KempHttpClient
,它更加可配置,而且我相信这是未来的趋势。 - Matthew((WebRequest) null).GetResponseWithoutException()
不会引起 NullReferenceException
,因为它被编译成等同于 WebRequestExtensions.GetResponseWithoutException(null)
的代码,这不会导致 NullReferenceException
,因此需要进行输入验证。 - Matthew有趣的是,从WebException.Response
获取的HttpWebResponse.GetResponseStream()
与从服务器收到的响应流不同。在我们的环境中,当使用HttpWebRequest/HttpWebResponse
对象将400 HTTP状态代码返回给客户端时,我们正在失去实际的服务器响应。据我们所见,与WebException的HttpWebResponse
相关联的响应流是在客户端生成的,并且不包括来自服务器的任何响应正文。这非常令人沮丧,因为我们想要向客户端传回错误请求的原因。
在尝试连接Google的OAuth2服务时,我遇到了类似的问题。
最终我手动编写了POST请求,而不是使用WebRequest,代码如下:
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编码的数据片段之间放置了换行符。当我将它们删掉后,一切都正常了。您可能能够使用类似的技术连接到您的服务并读取实际的响应错误文本。
尝试以下代码(VB 语言):
Try
Catch exp As WebException
Dim sResponse As String = New StreamReader(exp.Response.GetResponseStream()).ReadToEnd
End Try
扩展函数的异步版本:
public static async Task<WebResponse> GetResponseAsyncNoEx(this WebRequest request)
{
try
{
return await request.GetResponseAsync();
}
catch(WebException ex)
{
return ex.Response;
}
}
这对我解决了问题:
https://gist.github.com/beccasaurus/929007/a8f820b153a1cfdee3d06a9c0a1d7ebfced8bb77
简而言之:
问题:
本地主机返回预期内容,远程IP将400内容更改为“错误请求”
解决方案:
在web.config/configuration/system.webServer
中添加<httpErrors existingResponse="PassThrough"></httpErrors>
对我来说解决了这个问题;现在所有服务器(本地和远程)都返回由我生成的完全相同的内容,无论我返回的IP地址和/或HTTP代码是什么。