ASP.NET Web API removing HttpError from responses
Asked Answered
B

2

6

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.

Bareheaded answered 11/7, 2013 at 9:14 Comment(0)
N
2

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.

Noma answered 11/7, 2013 at 10:21 Comment(1)
Well, I guess this would be good, but in my application, I return my own HttpResponseMessages, sometimes as 400 Bad Request, for example, if someone wants to insert new record to my database, he uses controller PostNewRecord method. The record he inserts is passed through validation, which if it detects that some fields are missing, controller returns Bad Request with my own message that some fields were empty. In your solution, MyApiControllerActionInvoker would intercept that response, and remove my message, which I don't want to happen. I only want to remove Web API generated messages.Graces
R
1

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.

Repast answered 11/7, 2013 at 12:56 Comment(1)
That would of course work, but it's, as you said, rather fix, than long-term solution.Graces

© 2022 - 2024 — McMap. All rights reserved.