9

I am making an application where a client does requests to a server. The server is written in node.js and the client in .NET using flurl.Http.

When a request on the server fails, it is always usefull to create a custom error message. Something like this:

request.respond(500, { message: "targetid is mandatory" });

However this invokes a FlurlHttpException on the client side and the JSON information in the response gets lost.

How can I receive this JSON information when a non-successful response code is received from the server?

Luke Girvin
  • 13,221
  • 9
  • 64
  • 84
Cerebres
  • 343
  • 3
  • 16

3 Answers3

14

Since Flurl sort of specializes in getting from a URL to a deserialized response body in as few keystrokes as possible, it's often asked how to deal with error responses, where the body almost always take a different shape than successful ones. This is why Flurl defaults to throwing on non-2XX responses, so you can deal with error responses like this:

try {
    var t = await "http://api.com".GetJsonAsync<T>();
}
catch (FlurlHttpException ex) {
    var error = await ex.GetResponseJsonAsync<TError>();
}

There are a few variations:

ex.GetResponseStringAsync();
ex.GetResponseJsonAsync(); // returns a dynamic
Todd Menier
  • 37,557
  • 17
  • 150
  • 173
  • there is a setting `OnError`. Can I use it to wrap `FlurlHttpException` with another type and rethrow it somehow? btw thanks for an awesome framework. –  Apr 11 '19 at 14:16
  • Yes, and that's actually a pretty common use case for `OnError`. https://flurl.io/docs/configuration/#event-handlers – Todd Menier Apr 11 '19 at 17:03
  • Anybody know what the type in this example comes from? There seems to be no reference. – jaygeek Nov 18 '20 at 14:22
1

I believe is interesting to handle all http errors as exceptions. Not sure if is the best practice, but I gave it a try.

What I did is something like this:

public static async Task<User> LoginWithEmail (string email, string password){
  try{
    return await "http://myapi.com/login"
                 .AppendPathSegment ("login")
                 .SetQueryParams (new {email = email, password = password})
                 .WithHeader ("Accept", "application/json")
                 .GetJsonAsync<User> ();
  }catch (FlurlHttpException e) {
    return await e.Call.Response.Content.ReadAsStringAsync ()
                  .ContinueWith<User> ((contentAsync) => {
                       throw new MyApiException(JsonConvert.DeserializeObject<MyApiError> (contentAsync.Result)); });
  }
}

This allows you to handle the success and error cases like this:

async void FakeLogin()
{
  try{
       User user = await LoginWithEmail ("fakeEmail@me.com", "fakePassword");
  }
  catch(MyApiException e) {
       MyApiError = e.Error;
  }
}

Basically

For the FlurlHttpException case, I do a continuation for ReadAsStringAsync where I declare the continuation to return a User, but inside the continuation I always throw an exception.

Additionally

You could refactor the exception handling to be as short as:

catch (FlurlHttpException e) {
    return await MyApiException.FromFlurlException<User>(e);
}
Lay González
  • 2,901
  • 21
  • 41
  • What version of Visual Studio are you using? That *might* compile in the 2015 preview (haven't tried), but won't in any supported release because [you can't await inside a catch block](http://stackoverflow.com/questions/8868123/await-in-catch-block). – Todd Menier Jan 21 '15 at 19:51
  • I'm using Xamarin's .Net PCL 78, which I believe is .net 4.5. It compiles and it runs, but now I'm not sure is running as expected (asynchronous). – Lay González Jan 21 '15 at 20:05
  • 1
    @ToddMenier Now I can confirm that I was able to compile it because I was using Mono. Visual Studios' .Net won't allow to use await inside a catch block as you said. – Lay González Feb 05 '15 at 23:34
0

I resolved this problem by editing the sourcecode of Flurl. In the file FlurlMessageHandler.cs you'll find the method IsErrorCondition that checks if an error has occured. Whenever the response doesn't have the code 2xx this is seen as an error, this part I removed from te code and now I manually check if the response has a succes code.

Cerebres
  • 343
  • 3
  • 16