We are currently re-developing our web forms system into web API and MVC (this is new technology for us) So far, all seems to be ok, however we are struggling to send back errors from Web API application to the MVC application. We realise that we need to capture any exceptions and these are transformed into HTTP responses
Web API Product controller looks like this:
public HttpResponseMessage GetProducts()
{
BAProduct c = new BAProduct();
var d = c.GetProducts();
if (d == null)
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "This is a custom error message");
else
return Request.CreateResponse(HttpStatusCode.OK, d);
}
The MVC application will call the web API by the following code:-
public T Get<T>()
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(Config.API_BaseSite);
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("api/myapplicaton/products").Result;
response.EnsureSuccessStatusCode();
T res = response.Content.ReadAsAsync<T>().Result;
return (T)res;
}
}
What we are trying to achieve is when an HTTP error is received from the web API within the MVC application, the user is either redirected to a custom error page, or display the custom error message within the current view (depending on the severity of the error). The issue that we are having is that:-
How to we access the custom error message that we have sent back? ( from the sample code this would be "This is a custom error message", We have been through every attribute within res and cannot see this message)
Depending on the status code how do we capture this and redirect users to individual error pages, i.e. 404 page, 500 page and display the custom response message that was sent back. we have been down the global.asax route
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
Response.Clear();
HttpException httpException = exception as HttpException;
however our httpExecption is always NULL
We have searched etc, and as of yet, cannot find anything appropriate, hopefully someone can point us in the right direction.
You can customize how Web API handles exceptions by writing an exception filter. An exception filter is executed when a controller method throws any unhandled exception that is not an HttpResponseException exception.
Another way to handle controller level exceptions is by overriding the OnException() method in the controller class. This method handles all your unhandled errors with error code 500. It allows you to log an exception and redirect to the specific view. It does not require to enable the <customErrors> config in web.
Asp.Net Web API VS Asp.Net MVC Asp.Net MVC is used to create web applications that return both views and data but Asp.Net Web API is used to create full-blown HTTP services with an easy and simple way that returns only data, not view.
The Web API returns the data in various formats, such as JSON, XML and other format based on the accept header of the request. But the MVC returns the data in the JSON format by using JSONResult. The Web API supports content negotiation, self hosting. All these are not supported by the MVC.
The reason why your httpException instance is null is because the response.EnsureSuccessStatusCode();
method doesn't thrown an HttpException
which is what you are attempting to cast it to. It is throwing an HttpRequestException
which is different but has no easy way of getting more details (such as the status code for example).
As an alternative to calling this method you could test the IsSuccessStatusCode
boolean property and throw an HttpException
yourself:
public T Get()
{
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(Config.API_BaseSite);
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("api/myapplicaton/products").Result;
if (!response.IsSuccessStatusCode)
{
string responseBody = response.Content.ReadAsStringAync().Result;
throw new HttpException((int)response.StatusCode, responseBody);
}
T res = response.Content.ReadAsAsync<T>().Result;
return (T)res;
}
}
This HttpException
could now be caught in your Application_Error
and depending on the status code proceed with the handling:
protected void Application_Error()
{
var exception = Server.GetLastError();
var httpException = exception as HttpException;
Response.Clear();
Server.ClearError();
var routeData = new RouteData();
routeData.Values["controller"] = "Errors";
routeData.Values["action"] = "Http500";
routeData.Values["exception"] = exception;
Response.StatusCode = 500;
Response.TrySkipIisCustomErrors = true;
if (httpException != null)
{
Response.StatusCode = httpException.GetHttpCode();
switch (Response.StatusCode)
{
case 403:
routeData.Values["action"] = "Http403";
break;
case 404:
routeData.Values["action"] = "Http404";
break;
// TODO: Add other cases if you want to handle
// different status codes from your Web API
}
}
IController errorsController = new ErrorsController();
var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
errorsController.Execute(rc);
}
In this example I assume that you have an ErrorsController
with the respective actions (Http500, Http403, Http404, ...). The respective action will be invoked depending on the status code and you may return different views.
UPDATE:
You might want to capture additional artifacts of the HTTP request such as the reason phrase so that you display it in your error page. In this case you could simply write your own exception that will contain the information you need:
public class ApiException : Exception
{
public HttpStatusCode StatusCode { get; set; }
public string Reason { get; set; }
public string ResponseBody { get; set; }
}
that you could throw:
if (!response.IsSuccessStatusCode)
{
throw new ApiException
{
StatusCode = response.StatusCode,
Reason = response.ReasonPhrase,
ResponseBody = response.Content.ReadAsStringAync().Result,
};
}
and then work with this custom exception in your Application_Error
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With