user1140479
user1140479

Reputation:

Exception from MVC API to MVC site gives error

I have created an MVC (4) Web API which works fine. It has a simple login-method that throws an exception when the user cannot be found.

Besides the API I created a simple website that calls the API by HttpClient:

public T ExecutePost<T>(string apiUrl, IEnumerable<KeyValuePair<string, string>> postData) {
    HttpContent content = null;

    if (postData != null)
        content = new FormUrlEncodedContent(postData);

    var a = _client.PostAsync(apiUrl, content).ContinueWith(httpResponseMessage => 
        JsonConvert.DeserializeObject<T>(httpResponseMessage.Result.Content.ReadAsStringAsync().Result)
    );

   return a.Result;
}

You can call this method as

ExecutePost<User>("url_to_controller_action_api", list_with_keys_and_values_to_post)

When this method calls the API with the postData-fiels username and password (both are correct and known by the system) an object called User will be returned... Works like a charm. When I call this method with the wrong username and/or password, the API throws an exception (User not found), but the method ExecutePost throws an exception aswell and the web page shows a nice, yellow-isch, red-letter page with errors that the normal user does not understand. The reason is easy: The data sent back from the API is not the same as the data that can be put in the object User.

So, what I would like to do is deserialize the exception, from the API, in an object called Error and return that to the controller of the MVC website, so I can put the error "User not found" on the page with the correct design of the site.

What is the best way to do this? Try-catch in all actions? Doesn't feel right to me... So suggestions are more than welcome.

Most things I found were API-side stuff, but I want to fetch and handle the exception on the site-side.

Thanks in advance

Upvotes: 0

Views: 286

Answers (1)

Stephen Byrne
Stephen Byrne

Reputation: 7475

On your Web API when you detect an invalid login, make sure that an HttpResponseException gets thrown with a status code of 401 (HttpStatusCode.Unauthorized).

//login failed:
var resp = new HttpResponseMessage(HttpStatusCode.Unauthorized)
                {
                    Content = new StringContent("Invalid Username or password")
                };
            throw new HttpResponseException(resp);

In your calling code, you can then first check if httpResponseMessage.StatusCode==HttpStatusCode.OK before you attempt to deserialise the response into a User.

var a = _client.PostAsync(apiUrl, content).ContinueWith(httpResponseMessage => {
if (httpResponseMessage.Status!=HttpStatus.OK)
{
     string ErrorMessage = httpResponseMessage.Result.Content.ReadAsStringAsync();
     //and whatever you want to do with that error message here
}
else
{ 
 try
    {
       var user = JsonConvert.DeserializeObject<T>(httpResponseMessage.Result.Content.ReadAsStringAsync().Result);
    }
    catch(Exception ex)
    {
        //honest to goodness unrecoverable failure on the webapi.
        //all you can do here is fail gracefully to the caller.
    }
}//endif (Status OK)
}//end anonymous function
);

If it's not a 200 then you can execute a second check to see if it's a 401 (and have some specific handling for invalid login) or a more general 500 (something went wrong on the server), etc, and get the actual error message of the exception ("Invalid Username or password") to send back to the client:

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

Finally, even if it is 200 then you should still try...catch the deserialisation operation so you can gracefully handle any issues with the returned data, if for whatever reason the data can't be turned into a User object.

Upvotes: 1

Related Questions