WebException how to get whole response with a body?
Asked Answered
B

4

124

In WebException I cannot see body of GetResponse. This is my code in C#:

try {                
  return GetResponse(url + "." + ext.ToString(), method, headers, bodyParams);
} catch (WebException ex) {
    switch (ex.Status) {
      case WebExceptionStatus.ConnectFailure:
         throw new ConnectionException();                        
     case WebExceptionStatus.Timeout:
         throw new RequestTimeRanOutException();                     
     case WebExceptionStatus.NameResolutionFailure:
         throw new ConnectionException();                        
     case WebExceptionStatus.ProtocolError:
          if (ex.Message == "The remote server returned an error: (401) unauthorized.") {
              throw new CredentialsOrPortalException();
          }
          throw new ProtocolErrorExecption();                    
     default:
          throw;
    }

I see header but I don't see body. This is output from Wireshark for the request:

POST /api/1.0/authentication.json HTTP/1.1    
Content-Type: application/x-www-form-urlencoded    
Accept: application/json    
Host: nbm21tm1.teamlab.com    
Content-Length: 49    
Connection: Keep-Alive    

userName=XXX&password=YYYHTTP/1.1 500 Server error    
Cache-Control: private, max-age=0    
Content-Length: 106    
Content-Type: application/json; charset=UTF-8    
Server: Microsoft-IIS/7.5    
X-AspNet-Version: 2.0.50727    
X-Powered-By: ASP.NET    
X-Powered-By: ARR/2.5

Date: Mon, 06 Aug 2012 12:49:41 GMT    
Connection: close    

{"count":0,"startIndex":0,"status":1,"statusCode":500,"error":{"message":"Invalid username or password."}}

Is it possible somehow to see the message text in WebException? Thank you.

Barcus answered 6/8, 2012 at 13:2 Comment(2)
Have you tried (HttpWebResponse)we.Response; Where 'we' is your caught WebException?Dogmatize
To preserve stack trace in rethrown exception do not use throw ex; but simply throw; (in the default case). Additionally (if needed) I would put the original WebException in your custom Exceptions' InnerException (through appropriate constructor).Raster
U
228
var resp = new StreamReader(ex.Response.GetResponseStream()).ReadToEnd();

dynamic obj = JsonConvert.DeserializeObject(resp);
var messageFromServer = obj.error.message;
Ureide answered 6/8, 2012 at 13:6 Comment(6)
For anyone unfamiliar with JsonConvert, you need to get Newtonsoft.Json from nuget package manager.Agger
Please update answer with Kyle's explanation since Newtonsoft.Json is optionall.Accompanist
Also, please explain that this code should go within the Catch fallback clause of a Try-Catch codeblock in which the request should go. i know for this case it's obvious to the attention-paying reader and @iwtu, but Fully comprehensive answers can make the real difference to the beginners reading this answer;)Accompanist
StreamReader implements IDisposable, so isn't it best practice to wrap this in a using statement? A quick look at StreamReader's Dispose method suggests that it does some important cleanup in there.Bodkin
@Bodkin No worries, since there is no unmanaged code/data here in this case, Garbage collecter can handle it easily... (But using using is a good habit always )Ureide
I would point out that this only gets the response BODY. If there is a SSL/TLS error instead, there is no response body. You'll want a null-conditional ("?.") or two in your expressions.Hypogenous
G
46
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();
}
Genipap answered 16/1, 2014 at 6:18 Comment(0)
I
7

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;
}
Isentropic answered 9/8, 2018 at 14:40 Comment(0)
N
2

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.

Nonparticipation answered 13/5, 2021 at 16:43 Comment(2)
Instead of string.Empty, how about the exception Message property? Also check for a null GetResponseStream()Pedaiah
The answer can be modified to fit the specific problem. As for null checking, I haven't ran into a scenario where it was null, but yes it is better practice to null check.Nonparticipation

© 2022 - 2024 — McMap. All rights reserved.