为什么HttpWebRequest会抛出异常而不是返回HttpStatusCode.NotFound?

40

我正在尝试使用HttpWebRequest验证Url的存在性。 我找到了一些基本上执行此操作的示例:

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(Url);
request.Method = "HEAD";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
    return response.StatusCode;
}

然而,如果URL确实存在问题,它不会返回响应,而是抛出异常。

我将我的代码修改为以下内容:

try
{
    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(Url);
    request.Method = "HEAD";
    using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
    {
        return response.StatusCode;
    }
}
catch (System.Net.WebException ex)
{
    var response = ex.Response as HttpWebResponse;
    return response == null ? HttpStatusCode.InternalServerError : response.StatusCode;
}

这似乎最终做到了我想要的。

但是我想知道为什么请求会抛出异常而不返回带有未找到状态代码的响应?

3个回答

72

是的,当网页大量使用状态代码并且其中并不全部是错误时,这可能会非常令人烦恼。这可能会使处理正文变得非常困难。我个人使用此扩展方法获取响应。

public static class HttpWebResponseExt
{
    public static HttpWebResponse GetResponseNoException(this HttpWebRequest req)
    {
        try
        {
            return (HttpWebResponse)req.GetResponse();
        }
        catch (WebException we)
        {
            var resp = we.Response as HttpWebResponse;
            if (resp == null)
                throw;
            return resp;
        }
    }
}

7
虽然使用HttpWebRequest / Response来保存代码是最少工作的选择,但这是.Net Framework作者糟糕设计选择的一部分。正确的解决方案是使用HttpClient,该方案不会在4xx和5xx状态代码上抛出异常。异常是为了处理异常情况而设计的,在这种情况下仅仅是为了抛出并捕获它,然后像这样继续进行是丑陋的、对性能有害的,特别是因为有一种更好的选项可以完全避免这种情况。https://msdn.microsoft.com/zh-cn/library/hh138242(v=vs.118).aspx - Chris Moschini
4
这似乎不是真的。我正在一个项目中使用 HttpClient,当调用一个不存在的 URL 并返回 404 状态码时,客户端会抛出异常,而不是返回带有 404 状态码的响应。在使用 HttpClient 时是否有额外的步骤可以防止这种情况发生? - SelAromDotNet
在我的情况下,HttpClient 在 400 Bad Request 上抛出异常。 - Mariusz
在我的情况下,抛出了一个“AggregateException”,而“InnerException”是一个“WebException”。 - sdgfsdh

1

为什么不呢?它们都是有效的设计选项,而HttpWebRequest只是按照这种方式设计的。


只要代码出现4xx,你就可以阅读响应头和响应体。 - Ian Boyd
6
我猜我感到困惑,因为我看到的所有代码示例都没有考虑这一点。 很多甚至没有try/catch,我在想是否可能我错过了什么,有没有一种方法可以在不引发异常的情况下获取状态。如果状态码旨在处理这种状态,那么抛出整个异常似乎是不符合直觉的。 - SelAromDotNet
1
是的,当你认为“当然xxx测试了他放在网站上的代码行!”时,结果总是会很有趣,而你却错了 :) - Mahmoud Al-Qudsi
9
微软建议尽可能避免使用异常,因为它可能会对性能产生负面影响。http://msdn.microsoft.com/en-us/library/ms229009.aspx - Ramesh
13
这是我认为这个不直观的另一个原因。如果响应可以捕获404状态码,那么我认为这不应该被视为异常。请求已经完成了,它只是没有找到任何东西。 - SelAromDotNet
1
@MahmoudAl-Qudsi,我很高兴他们没有决定对3xx和20x返回异常,仅仅因为你可以在以后捕获它们!404不是一个异常,显然也不是客户端的异常,而是另一台需要处理异常的服务器端机器。在我的一侧出现异常意味着我做错了什么! - AaA

0

就像@Will一样,我编写了类似的扩展方法,从WebException中获取响应内容字符串。


        /// <summary>
        /// Reads Response content in string from WebException
        /// </summary>
        /// <param name="webException"></param>
        /// <returns></returns>
        public static (HttpStatusCode statusCode, string? responseString) GetResponseStringNoException(this WebException webException)
        {
            if (webException.Response is HttpWebResponse response)
            {
                Stream responseStream = response.GetResponseStream();

                StreamReader streamReader = new(responseStream, Encoding.Default);

                string responseContent = streamReader.ReadToEnd();
                HttpStatusCode statusCode = response.StatusCode;

                streamReader.Close();
                responseStream.Close();
                response.Close();

                return (statusCode, responseContent);
            }
            else
            {
                return (HttpStatusCode.InternalServerError, null);
            }
        }


以上代码是非优化的解决方案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接