Per Request Error Handling Policy

199 Views Asked by At

I'm attempting to follow this 2013 guide on setting up per-request error handling policies.

  • If the request is local, display the error information.
  • If the current user is in the IT group, display the error information.
  • For everyone else, don't show errors.

My code:

public class PerRequestErrorPolicyDelegatingHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Properties[HttpPropertyKeys.IncludeErrorDetailKey] = new Lazy<bool>(() =>
        {
            // CHECK USER
            var identity = request.GetRequestContext().Principal as ClaimsPrincipal;
            return identity != null && identity.Identity.IsAuthenticated && identity.IsInRole("IT");
        });

        return base.SendAsync(request, cancellationToken);
    }
}

It's registered as the first handler:

public static void Configure(HttpConfiguration http)
{
    http.MessageHandlers.Add(new PerRequestErrorPolicyDelegatingHandler());
    ...
}

My Web Api is hosted on Owin, but I see no relevant Owin code, so I've omitted it. To test, I'm using this controller:

[AllowAnonymous]
[RoutePrefix("api/sandbox")]
public class SandboxApiController : ApiController
{
    [HttpPost, Route("test")]
    public IHttpActionResult Test()
    {
        throw new InvalidOperationException($"you screwed up. Local: {RequestContext.IsLocal}, IT: {User.IsInRole("IT")}");
    }
}

Locally, I always get error information, and the lazy-loading code (denoted by "CHECK USER") is never executed, so I can't figure out how to debug it.

Deployed to a server, authenticated or not, I am never getting any error information.

I have no custom error configuration in my web.config.

What am I doing incorrectly?

1

There are 1 best solutions below

0
On BEST ANSWER

I decided that the guide I was following was too old to be relevant anymore, and followed this approach instead. It's simpler, and works.

We replace the ExceptionResult with a new one, where we specify whether the detail should be included. We copy the other dependencies from the existing result.

public class PerUserErrorDetailExceptionHandler : ExceptionHandler
{
    public override void Handle(ExceptionHandlerContext context)
    {
        var exResult = context.Result as ExceptionResult;
        if (!context.CatchBlock.IsTopLevel || exResult == null)
            return;

        var identity = context.RequestContext.Principal as ClaimsPrincipal;
        var showErrorDetail = identity != null
                              && identity.Identity.IsAuthenticated
                              && identity.IsInRole("IT");

        context.Result = new ExceptionResult(
            exResult.Exception,
            showErrorDetail,
            exResult.ContentNegotiator,
            exResult.Request,
            exResult.Formatters);
    }
}

Elsewhere, in the Web Api configuration:

public static void Configure(HttpConfiguration http)
{
    http.Services.Replace(typeof(IExceptionHandler), new PerUserErrorDetailExceptionHandler());
}