WEB API httpException is not caught

353 Views Asked by At

I have a WEB API application, where we add some session verification logic and then call base SendAsync method after that.

When we pass request containing byte[] larger than 4MB line response = await base.SendAsync(request, cancellationToken); throws a httpException "maximum request length exceeded", which is ok and can be managed through IIS settings. What bothers me is that next step - it's still calling controller method, where I can see that this byte[] parameter set to null. Controller method throws ArgumentNullException and this what end user gets.

First of all I can't understand why controller method still gets called after HttpException. And more important how can I manage to catch this HttpException, so that end user will see actual reason, why the request was not processed. Thank you in advance.

public class SessionIdHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        //some additional logic here
        HttpResponseMessage response;
        try
        {
            response = await base.SendAsync(request, cancellationToken);
        }
        catch (Exception e)
        {
            response = new HttpResponseMessage(HttpStatusCode.Forbidden)
            {
                Content = new StringContent(e.Message, Encoding.UTF8, "application/json")
            };
        }
        finally
        {
            if (signedOnHere)
            {
                Signoff(sessionId);
            }
        }
        return response;
   }
}
1

There are 1 best solutions below

3
On BEST ANSWER

What bothers me is that next step - it's still calling controller method, where I can see that this byte[] parameter set to null.

When you call base.SendAsync(request, cancellationToken);, you're effectively continuing the WebAPI pipeline, which will eventually results in the controller getting invoked:

WebAPI pipeline

What you actually want is to check the Content-Length header before you even send the request to the controller. You do so by retrieving it for the header of the request. If the size exceeds, you can return a response indicating that, otherwise, continue the pipeline:

protected override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, 
            CancellationToken cancellationToken)
{
    // 4MB, perhaps you want to loosen this up a bit as other request
    // characteristics will effect the size.
    const int maxRequestSize = 4194304;

    IEnumerable<string> contentLengthHeader;
    request.Headers.TryGetValues("Content-Length", out contentLengthHeader);

    var contentLength = contentLengthHeader.FirstOrDefault();
    if (contentLength == null)
    {
        //No content-length sent, decide what to do.
    }

    long actualContentlength;
    if (!long.TryParse(contentLength, out actualContentlength))
    {
        // Couldn't parse, decide what to do.
    }

    if (actualContentlength > maxRequestSize)
    {
        // If reached, the content-length of the request was too big. 
        // Return an error response:
        return Task.FromResult(request.CreateErrorResponse(HttpStatusCode.Forbidden,
            string.Format("Request size exceeded {0} bytes", maxRequestSize)));
    }
    return base.SendAsync(request, cancellationToken);
}

If you don't want this check for all the controllers in your WebAPI application, you can pre-route each DelegatingHandler.