0

ASP.NET / Mono MVC4 Web API v.1 application.

API controllers are using Forms authorication and are decorated with standard [Authorize] attribute. If authorization fails, standard api error message

<Error>
<Message>Authorization has been denied for this request.</Message>
</Error>

occurs. How to itercept this error for sriting to log file . Error message returned to caller shoudl remain the same. how to add additional code to this which can wrote this error message with whole http request headers and body to log file ?

I added code from question

How do I log ALL exceptions globally for a C# MVC4 WebAPI app?

and from

How to catch undefined api method calls in ASP.NET MVC4 Web API

but athorization error is not catched.

How to catch all errors ?

Update

code needs to run in Windows 2003 server. I tried code from answer but got compile errors

Predefined type 'System.Runtime.CompilerServices.IAsyncStateMachine' is not defined or imported

Cannot find all types required by the 'async' modifier. Are you targeting the wrong framework version, or missing a reference to an assembly?

Cannot find all types required by the 'async' modifier. Are you targeting the wrong framework version, or missing a reference to an assembly?

How to run int in W2003 server ?

Community
  • 1
  • 1
Andrus
  • 26,339
  • 60
  • 204
  • 378

1 Answers1

7

One way you could achieve this is to write a DelegatingHandler to intercept the response before it gets sent back to the client, and then log information on those requests that return errors.

public class RepsonseInterceptor : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var response = await base.SendAsync(request, cancellationToken);

        LogResponse(request, response);

        return response;
    }

    public void LogResponse(HttpRequestMessage request, HttpResponseMessage response)
    {
        HttpStatusCode status = response.StatusCode;
        //Catch the status codes you want to Log
        if (status == HttpStatusCode.NotFound || status == HttpStatusCode.Unauthorized || status == HttpStatusCode.InternalServerError)
        {
            //Do Logging Stuff here
            //with the Request and Response
        }
    }
}

Then add this to the Application_Start in the Global.asax.cs:

GlobalConfiguration.Configuration.MessageHandlers.Add(new ResponseInterceptor());

EDIT:

If you don't want to execute the handler for all routes you can add it as a Per-Route Message Handler to those routes you want instead of globally like so:

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional },
    constraints: null,
    handler: new ResponseInterceptor()
    );

If you are using .NET Framework 4.0 you need to change the SendAsync method of the handler as follows:

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
    return base.SendAsync(request, cancellationToken)
      .ContinueWith(task =>
      {
          var response = task.Result;
          LogResponse(request, response);
          return response;
       });
}

I think it would be reasonable to just use this MessageHandler if you can obtain all of the information that you need to log from the Request and Response objects, although I've not fully tested this out.

Jon Susiak
  • 4,948
  • 1
  • 30
  • 38
  • Thank you. It looks like this intercepts errors which occurs in other controllers also, not only in /api/ controller and it catches handlers from other handlers in question also. I'm looking for a way to log all 4xx and 5xx responses. Is it reasonable to remove Application_Error handlers and handlers shown in question and use only this as global error handler? Will it catch all 4xx and 5xx return codes ? Will it work in .NET 4 ? Code needs to run in windows 2003 server so I cannot move to .NET 4.5 – Andrus Nov 29 '13 at 10:43
  • It tried to compile it for windows 2003 server but got compile errors. How to use it in Windows 2003 server ? I updated question – Andrus Nov 29 '13 at 10:54
  • How to get last exception for logging? In `Application_Error` we can use `Server.GetLastError()` but in SendAsync Server object is not defined. – Andrus Nov 29 '13 at 12:53