WebException how to get whole response with a body?

try {
 WebClient client = new WebClient();
 client.Encoding = Encoding.UTF8;
 string content = client.DownloadString("https://sandiegodata.atlassian.net/wiki/pages/doaddcomment.action?pageId=524365");
 Console.WriteLine(content);
 Console.ReadKey();
} catch (WebException ex) {
 var resp = new StreamReader(ex.Response.GetResponseStream()).ReadToEnd();
 Console.WriteLine(resp);
 Console.ReadKey();
}

I don't see any answers with using statements, and I don't see any uses of async.

public static class WebExceptionExtensions
{
    public static string GetResponseBody(this WebException webException)
    {
        if (webException.Status == WebExceptionStatus.ProtocolError)
        {
            try
            {
                using (var stream = webException.Response.GetResponseStream())
                {
                    if (stream is null)
                        return string.Empty; // or webException.Message
                    using (var reader = new StreamReader(stream))
                    {
                        string msg = reader.ReadToEnd();
                        if (string.IsNullOrEmpty(msg) && webException.Response is HttpWebResponse response)
                            msg = $"{response.StatusDescription} ({(int)response.StatusCode})"; // provide some error message if not found

                        return msg;
                    }
                }
            }
            catch (WebException) // we tried
            {
                return string.Empty; // or webException.Message
            }
        }
        else
        {
            return string.Empty; // or webException.Message
        }
    }

    public static async Task<string> GetResponseBodyAsync(this WebException webException)
    {
        if (webException.Status == WebExceptionStatus.ProtocolError)
        {
            try
            {
                using (var stream = webException.Response.GetResponseStream())
                {
                    if (stream is null)
                        return string.Empty; // or webException.Message
                    using (var reader = new StreamReader(stream))
                    {
                        string msg = await reader.ReadToEndAsync();
                        if (string.IsNullOrEmpty(msg) && webException.Response is HttpWebResponse response)
                            msg = $"{response.StatusDescription} ((int){response.StatusCode})"; // provide some error message if not found

                        return msg;
                    }
                }
            }
            catch (WebException) //  we tried
            {
                return string.Empty; // or webException.Message
            }
        }
        else
        {
            return string.Empty; // or webException.Message
        }
    }
}

Now, whenever we catch WebExceptions, it is very easy to get the response body.

try 
{
    // Do work here...
}
catch (WebException we)
{
    Console.WriteLine(we.GetResponseBody()); // synchronous
    Console.WriteLine(await we.GetResponseBodyAsync()); // or asynchronous
}
catch (Exception e)
{
    throw new Exception("Unexpected error occured", e);
}

Be Warned, if you attempt to call this method twice, you will get an exception that the stream was already disposed of. This extension method is really only good for showing an error quickly and moving on. If you need extended logic, you'll likely want to make your own method.


This only improves on the existing answers. I have written a method that takes care of the details of throwing/rethrowing with an enhanced message, that includes the response body:

Here's my code (in Client.cs):

/// <summary>
///     Tries to rethrow the WebException with the data from the body included, if possible. 
///     Otherwise just rethrows the original message.
/// </summary>
/// <param name="wex">The web exception.</param>
/// <exception cref="WebException"></exception>
/// <remarks>
///     By default, on protocol errors, the body is not included in web exceptions. 
///     This solutions includes potentially relevant information for resolving the
///     issue.
/// </remarks>
private void ThrowWithBody(WebException wex) {
    if (wex.Status == WebExceptionStatus.ProtocolError) {
        string responseBody;
        try {
            //Get the message body for rethrow with body included
            responseBody = new StreamReader(wex.Response.GetResponseStream()).ReadToEnd();

        } catch (Exception) {
            //In case of failure to get the body just rethrow the original web exception.
            throw wex;
        }

        //include the body in the message
        throw new WebException(wex.Message + $" Response body: '{responseBody}'", wex, wex.Status, wex.Response);
    }

    //In case of non-protocol errors no body is available anyway, so just rethrow the original web exception.
    throw wex;
}

You use it in a catch clause much like the OP showed:

//Execute Request, catch the exception to eventually get the body
try {
    //GetResponse....
    }
} catch (WebException wex) {
    if (wex.Status == WebExceptionStatus.ProtocolError) {
        ThrowWithBody(wex);
    }

    //otherwise rethrow anyway
    throw;
}

var resp = new StreamReader(ex.Response.GetResponseStream()).ReadToEnd();

dynamic obj = JsonConvert.DeserializeObject(resp);
var messageFromServer = obj.error.message;