Refit: Disable Refit from hiding HTTP content in case response code wasn't 200 (ApiException)

Created on 23 Apr 2016  Â·  16Comments  Â·  Source: reactiveui/refit

We use http code to indicate what's wrong and send reason in json response. Currently Refit overrides this behavior with this message "Response status code does not indicate success: 400 (Bad Request)"

outdated

Most helpful comment

By the way, with the above GroupList method, and assuming an error response like this:

public class ErrorResponse
{
    public string Message { get;set; }
}

All you would have to do is:

try
{
    var users = api.GroupList(groupId);
    // do something with users
}
catch(ApiException ex)
{
   var statusCode = ex.StatusCode;
   var error = ex.GetContentAs<ErrorResponse>();
   // deal with error.Message or something
}

You still get the automatic mapping for successful cases, and I don't see how it adds much work when it fails. Can you explain why this isn't enough, and more importantly - exactly how you see it working otherwise?

All 16 comments

Why not just catch the exception and check the status code?

If you want the raw HttpResponseMessage, just change the return type on your method to Task<HttpResponseMessage> (see https://github.com/paulcbetts/refit#retrieving-the-response).

Hmm.. but that way I'll lose the magic of mapping response to my DTO and will have to do it manually :)

Most of the properties of a response that you would need are available on the ApiException class. There's even a method to deserialize the error response for you. GetContentAs<T>().

That's all great, but this means I will change all my Refit from the elegant

[Get("/group/{id}/users")]
Task<List<User>> GroupList([AliasAs("id")] int groupId);

to

Task<List<HttpResponseMessage>> GroupList([AliasAs("id")] int groupId);

Which kinda beats the purpose of Refit in the first place.

Let me share with you my implementation. I am using ASP.NET WebAPI, and I am enapsulating all my responses in a standard Json envelope:

public class JsonResponseResult<T>
    {
        //public string Version { get { return "1.2.3"; } }
        public bool Success => string.IsNullOrEmpty(ErrorMessage);
        public string ErrorMessage { get; set; }
        public T Result { get; set; }
        public JsonResponseResult(T result = default(T), string errorMessage = null)
        {
            Result = result;
            ErrorMessage = errorMessage;
        }
    }

Which the mobile has an identical one over. so my Refit is like:

[Headers ("Accept: application/json")]
public interface IMyApi
{
    [Get ("/api/entities")]
    Task<JsonResponseDto<MyDto>> Get();
}

and on mobile, I can easily check validity by doing

var response = await APIs.Get();
if (Response.Success)
{
   ///YAAAYY
}
else
{
   //BOOO
}

Does Refit really don't deserialize the content to my Object if the return code is not 200? This will make it unusable for me. For my understanding of REST you should use meaningful status codes e.g. 403 but then it might be necessary to include a reason object in the response so that the client can deal with it appropriately.

Exactly my point @escamoteur. As a workaround (more of a hack, really), I
forced all my HTTP responses to be 200 to bypass this limitation, and I set
REAL HTTP status code in API envelop property so the mobile can interpret
what really happened. It's only possible because in this specific project I
have control over backend and mobile.

On Sun, Apr 24, 2016 at 5:58 PM, escamoteur [email protected]
wrote:

Does Refit really don't deserialize the content to my Object if the return
code is not 200? This will make it unusable for me. For my understanding of
REST you should use meaningful status codes e.g. 403 but then it might be
necessary to include a reason object in the response so that the client can
deal with it appropriately.

—
You are receiving this because you authored the thread.
Reply to this email directly or view it on GitHub
https://github.com/paulcbetts/refit/issues/231#issuecomment-213989273

You guys really return the same type from your API no matter what the status code is?

Haven't said that, but we want to return data even if an request failed.

Okay, so how do you see it working? What would your method definition look like on the interface?

IMHO it should be possible to disable the Exception and offer a way to examine the response object including the Status code.

@escamoteur @Korayem What's the problem with just using ApiException.GetContentAs<T>()?

@escamoteur & @Korayem:

If your method has this signature:

[Get("/group/{id}/users")]
Task<List<User>> GroupList([AliasAs("id")] int groupId);

And Refit doesn't throw an exception on an error status, _where will the error content go, and how will you know an error has occurred?_ You're saying Refit should deserialize it for you but how do we know what it should be deserialized as?

You obviously would need to change the return type _somehow_, so what are you suggesting? (Bear in mind that output parameters are not an option because they don't work with async.)

By the way, with the above GroupList method, and assuming an error response like this:

public class ErrorResponse
{
    public string Message { get;set; }
}

All you would have to do is:

try
{
    var users = api.GroupList(groupId);
    // do something with users
}
catch(ApiException ex)
{
   var statusCode = ex.StatusCode;
   var error = ex.GetContentAs<ErrorResponse>();
   // deal with error.Message or something
}

You still get the automatic mapping for successful cases, and I don't see how it adds much work when it fails. Can you explain why this isn't enough, and more importantly - exactly how you see it working otherwise?

If it works like this, this would be fine for me. Would be a good Idea to add this to the documentation.
What I would like too now too is if there is a possibility to access the last response object if deserilisation was succesful.

At first I thought you suggested to change all my API signatures but now it's simple enough @bennor I will wrap the calls as you suggested.

You solution deserves to be mentioned in the documentation :)

For those following similar approach as mine where I envelope all API responses, to implement @bennor solution, I added following to my api calls

....
} catch (ApiException ex) {
    var error = ex.GetContentAs<JsonResponseDto> ();
    errorMessage = error.ErrorMessage;
}
....
Was this page helpful?
0 / 5 - 0 ratings

Related issues

omares picture omares  Â·  6Comments

ColKrumpler picture ColKrumpler  Â·  3Comments

mary-perret-1986 picture mary-perret-1986  Â·  5Comments

Mike-E-angelo picture Mike-E-angelo  Â·  6Comments

SWarnberg picture SWarnberg  Â·  4Comments