I'm building RESTful service using Microsoft ASP.NET Web API.
My problem concerns HttpErrors that Web API throws back to user when something go wrong (e.g. 400 Bad Request or 404 Not Found).
The problem is, that I don't want to get serialized HttpError in response content, as it sometimes provides too much information, therefore it violates OWASP security rules, for example:
Request:
http://localhost/Service/api/something/555555555555555555555555555555555555555555555555555555555555555555555
As a response, I get 400 of course, but with following content information:
{
"$id": "1",
"Message": "The request is invalid.",
"MessageDetail": "The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'MyNamespaceAndMethodHere(Int32)' in 'Service.Controllers.MyController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}
Something like this not only indicates that my WebService is based on ASP.NET WebAPI technology (which isn't that bad), but also it gives some information about my namespaces, method names, parameters, etc.
I tried to set IncludeErrorDetailPolicy in Global.asax
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Never;
Yeah, that did somehow good, now the result doesn't contain MessageDetail section, but still, I don't want to get this HttpError at all.
I also built my custom DelegatingHandler, but it also affects 400s and 404s that I myself generate in controllers, which I don't want to happen.
My question is: Is there any convinient way to get rid of serialized HttpError from response content? All I want user to get back for his bad requests is response code.
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.
Return InternalServerError for Handled Exceptionscs file and locate the Get(int id) method. Add the same three lines within a try... catch block, as shown in Listing 2, to simulate an error. Create two catch blocks: one to handle a DivideByZeroException and one to handle a generic Exception object.
To apply the exception filter to all Web API controllers, the filter needs to register to GlobalConfiguration. Configuration. Filters collection. Following is a snapshot of Fiddler, when unhandled execution occurred in the action method.
What about using a custom IHttpActionInvoker ? Basically, you just have to send an empty HttpResponseMessage.
Here is a very basic example :
public class MyApiControllerActionInvoker : ApiControllerActionInvoker
{
public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken)
{
var result = base.InvokeActionAsync(actionContext, cancellationToken);
if (result.Exception != null)
{
//Log critical error
Debug.WriteLine("unhandled Exception ");
return Task.Run<HttpResponseMessage>(() => new HttpResponseMessage(HttpStatusCode.InternalServerError));
}
else if (result.Result.StatusCode!= HttpStatusCode.OK)
{
//Log critical error
Debug.WriteLine("invalid response status");
return Task.Run<HttpResponseMessage>(() => new HttpResponseMessage(result.Result.StatusCode));
}
return result;
}
}
In Global.asax
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpActionInvoker), new MyApiControllerActionInvoker());
One other important thing you could do, not related to Web Api, is to remove excessive asp.net & IIS HTTP headers. Here is a good explanation.
I believe your approach of using the message handler is correct because regardless of the component in the Web API pipeline that sets the status code to 4xx, message handler can clear out response body. However, you do want to differentiate between the ones you explicitly set versus the ones set by the other components. Here is my suggestion and I admit it is a bit hacky. If you don't get any other better solution, give this a try.
In your ApiController
classes, when you throw a HttpResponseException
, set a flag in request properties, like so.
Request.Properties["myexception"] = true;
throw new HttpResponseException(...);
In the message handler, check for the property and do not clear the response body, if the property is set.
var response = await base.SendAsync(request, cancellationToken);
if((int)response.StatusCode > 399 && !request.Properties.Any(p => p.Key == "myException"))
response.Content = null;
return response;
You can package this a bit nicely by adding an extension method to HttpRequestMessage so that neither the ApiController
nor the message handler knows anything about the hard-coded string "myException" that I use above.
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