0

I'm trying to get a custom errorid (hashed guid) to an HttpError when exceptions are raised through an application. So a message like "xxxxxx - error" is passed to the client.

We have a custom ExceptionFilterAttribute to catch and log exceptions:

public class ExceptionFilter : ExceptionFilterAttribute
{
    ...
    public override void OnException(HttpActionExecutedContext context)
    {
        Guid guid = Guid.NewGuid();
        context.Exception.Data.Add("guid", guid);
        _log.WriteEvent(message, context.Exception, guid);
    }
}

A custom MediaTypeFormatter to send this (among other types) as json back to the client:

public class DotNetJsonFormatter : MediaTypeFormatter
{
    ...
    public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)
    {
        return Task.Factory.StartNew(() =>
        {
            if (typeof(Exception).IsAssignableFrom(type)) {
                // write a json string to the stream containing the message and id
            } else if (typeof(HttpError).IsAssignableFrom(type)) {
                // write a json string to the stream containing the message and id
            }
        });
    }
}

My problem lies with HttpError - all it contains is a dictionary containing the message, type, stack trace etc - I thought setting the data might pull this through (I've checked the HttpError constructor and that's all that's pulled from the exception).

  • I can't re-write the application to throw custom exceptions so the id ends up in the message.
  • I 'could' using reflection, alter the exception message to contain the id in the filter but it feels hacky.
  • Thought about re-throwing a new exception with the id in the message from the filter but this seems to bypass the formatter altogether
  • Tried setting the Exception property of the filter to set the underlying exception but it has no effect.

Am I stuck with the reflection technique?

Community
  • 1
  • 1
Phil Cooper
  • 3,083
  • 39
  • 63

1 Answers1

0

I hate answering my own questions but I will anyway.

So one option I didn't consider (too much inside box thinking) was setting the response in the filter rather than letting the formatter handle it.

The updated filter:

public class ExceptionFilter : ExceptionFilterAttribute
{
    public ExceptionFilter(ILogger log, IClientExceptionFormatter exceptionFormatter)
    {
        ...
        _log = log;
        _exceptionFormatter = exceptionFormatter;
    }

    public override void OnException(HttpActionExecutedContext context)
    {
        ...
        _log.Error(context.Exception);
        context.Response = new ExceptionJsonResponse(context.Exception, _exceptionFormatter);
    }
    ...
}

The ExceptionJsonResponse class:

public class ExceptionJsonResponse : HttpResponseMessage
{
    public ExceptionJsonResponse(Exception exception, IClientExceptionFormatter formatter)
    {
        ...
        _exception = exception;
        _formatter = formatter;

        Content = CreateContent();
        StatusCode = HttpStatusCode.InternalServerError;
    }

    private HttpContent CreateContent()
    {
        HttpContent content = new StringContent(_formatter.GetJson(_exception));
        content.Headers.ContentType = new MediaTypeHeaderValue(Common.ContentType.ApplicationJson);
        return content;
    }
    ...
}

This way bypasses the formatter probably because the response is a HttpResponseMessage. I can then alter the client message to my hearts content.

Phil Cooper
  • 3,083
  • 39
  • 63