18

I created some web apis and when an error happens the api returns HttpResponseMessage that is created with CreateErrorResponse message. Something like this:

return Request.CreateErrorResponse(
              HttpStatusCode.NotFound, "Failed to find customer.");

My problem is that I cannot figure out how to retrieve the message (in this case "Failed to find customer.") in consumer application.

Here's a sample of the consumer:

private static void GetCustomer()
{
    var client = new HttpClient();
    client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));
    string data =
        "{\"LastName\": \"Test\", \"FirstName\": \"Test\"";

    var content = new StringContent(data, Encoding.UTF8, "application/json");

    var httpResponseMessage = 
                 client.PostAsync(
                    new Uri("http://localhost:55202/api/Customer/Find"),
                    content).Result;
    if (httpResponseMessage.IsSuccessStatusCode)
    {
        var cust = httpResponseMessage.Content.
                  ReadAsAsync<IEnumerable<CustomerMobil>>().Result;
    }
}

Any help is greatly appreciated.

j0k
  • 22,600
  • 28
  • 79
  • 90
Vadim
  • 21,044
  • 18
  • 65
  • 101
  • Can you post the code where you're actually trying to read the error message? In this case the content of the message should really be an ObjectContent with an HttpError object - refer to the official page: http://msdn.microsoft.com/en-us/library/jj127065(v=vs.108).aspx Either way you have to vary the way you're reading the successful response and the error response as one is obviously in your case StreamContent, and the other should be ObjectContent – Joanna Derks Jan 31 '13 at 09:20
  • @JoannaTurban - this is the code. I couldn't figure out how to read the error message that's why I posted the question. So far you've been the most helpful person but I still didn't solve my problem. I'm trying more things right following your link. I was sidetracked and didn't work on this problem. But now I'm try to figure it out again. – Vadim Jan 31 '13 at 12:50
  • can you try the code in Update2 below and see if in case of an error you still get StreamContent ? – Joanna Derks Jan 31 '13 at 13:10
  • I assume that you want to see the type of the content. Here's the output. "content was of type System.Net.Http.StreamContent." I had to add {0} to output the type. – Vadim Jan 31 '13 at 13:17
  • OK, another question then - when do you create an error response? In what condition? Is it just in case of exceptions ? Note that `IsSuccessfulStatusCode` evaluates to `true` only if the status code was within `200-299`. Could it be that for a specific status code, say 400, you'd expect an error message but actually it never goes through the method/filter which is creating one? Maybe add some logging to see if the method to createErrorResponse has been called at all – Joanna Derks Jan 31 '13 at 13:54
  • When I call the api using Fiddler or REST Console, I see the message. In this particular case I set HttpStatus to 400 (return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Failed to find customer.");) Everything works as I expect. I just have no clue how to retrieve my custom message when an error occurs. – Vadim Jan 31 '13 at 14:33
  • @JoannaTurban - I just realized that I was missing instantiating the HttpClient. Just updated the code. – Vadim Jan 31 '13 at 15:31
  • See the last update - this time it should really work, I just realised why I was getting confused – Joanna Derks Jan 31 '13 at 15:57

5 Answers5

31

Make sure you set the accept and or content type appropriately (possible source of 500 errors on parsing the request content):

client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

content.Headers.ContentType = new MediaTypeWithQualityHeaderValue("application/json");

Then you could just do:

var errorMessage = response.Content.ReadAsStringAsync().Result;

That's all on the client of course. WebApi should handle the formatting of the content appropriately based on the accept and/or content type. Curious, you might also be able to throw new HttpResponseException("Failed to find customer.", HttpStatusCode.NotFound);

Jeff
  • 551
  • 3
  • 8
7

One way to get the message is to do:

((ObjectContent)httpResponseMessage.Content).Value

This will give you a dictionary that contains also the Message.

UPDATE

Refer to the official page:

http://msdn.microsoft.com/en-us/library/jj127065(v=vs.108).aspx

You have to vary the way you're reading the successful response and the error response as one is obviously in your case StreamContent, and the other should be ObjectContent.

UPDATE 2

Have you tried doing it this way ?

if (httpResponseMessage.IsSuccessStatusCode)
    {
        var cust = httpResponseMessage.Content.
                  ReadAsAsync<IEnumerable<CustomerMobil>>().Result;
    }
else
{
   var content = httpResponseMessage.Content as ObjectContent;

   if (content != null)
    {
       // do something with the content
       var error = content.Value;
    }
   else
   {
      Console.WriteLine("content was of type ", (httpResponseMessage.Content).GetType());
   }

}

FINAL UPDATE (hopefully...)

OK, now I understand it - just try doing this instead:

httpResponseMessage.Content.ReadAsAsync<HttpError>().Result;

Joanna Derks
  • 4,033
  • 3
  • 26
  • 32
  • Unfortunately, when I try this I get InvalidCastException. Message=Unable to cast object of type 'System.Net.Http.StreamContent' to type 'System.Net.Http.ObjectContent'. – Vadim Jan 28 '13 at 14:54
  • @Vadim - try reading the stream then `((StreamContent)httpResponseMessage.Content).ReadAsAsync().Result` (or whatever type you expect - try object and see what you get as a result maybe) – Joanna Derks Jan 28 '13 at 15:03
  • that's exactly what I'm doing to retrieve data and it works fine. But I want to retrieve error message. – Vadim Jan 28 '13 at 16:36
  • 1
    Joanna is right here. If you're returning the value of CreateErrorResponse, the response message content should be an ObjectContent. If it's actually a StreamContent, then you're returning something else. – Youssef Moussaoui Jan 28 '13 at 16:38
2

This is an option to get the message from the error response that avoids making an ...Async().Result() type of call.

((HttpError)((ObjectContent<HttpError>)response.Content).Value).Message

You should make sure that response.Content is of type ObjectContent<HttpError> first though.

Ben Bartle
  • 1,080
  • 11
  • 12
1

It should be in HttpResponseMessage.ReasonPhrase. If that sounds like a bit of a strange name, it's just because that is the way it is named in the HTTP specification http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html

Darrel Miller
  • 139,164
  • 32
  • 194
  • 243
  • ReasonPhrase comes back as the statusCode though, so in case of 500 I'm getting 'InternalServerError' – Joanna Derks Jan 28 '13 at 15:31
  • @JoannaTurban Oh, that sucks. I was hoping the CreateErrorResponse would set the ReasonPhrase. Well, you could either set the ReasonPhrase manually on the server, or you can deserialize the JSON/XML body on the client. – Darrel Miller Jan 28 '13 at 15:35
0

OK this is hilarious, but using QuickWatch I came up with this elegant solution:

(new System.Collections.Generic.Mscorlib_DictionaryDebugView(((System.Web.Http.HttpError)(((System.Net.Http.ObjectContent)(httpResponseMessage.Content)).Value)))).Items[0].Value

That is super readable!

cerhart
  • 381
  • 3
  • 17