0

I put external in brackets, while it is not completely true. Here's the explanation:

I've got a MVC ASP.NET application (one solution) that consists of the following projects:

  1. BusinessLogic
  2. WebApi (pure WebApi2 controllers, returning json-serialized data based on (1))
  3. Frontend (MVC, Razor views, etc)

Frontend depends on webapi project and Frontend's RouteConfig registers routes for itself and for webapi project:

//register frontend routes, default code
routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
//calls webapi project to register its routes:
//var config = GlobalConfiguration.Configuration;
//... call webApi project
config.Routes.MapHttpRoute(
    name: "AppDefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
    name: "AppCustomApi",
    routeTemplate: "api/{controller}/{action}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

In this configuration I can deploy and run just the Frontend and access functionality as following:

Now I've got an issue registering exceptions happened in WebApi project. Error and Exception handling works perfectly in FrontEndProject:

These exceptions and errors I can catch via various ways (currently I utilize custom implementation of HttpModule)

Now, here is an issue. Exception and error handling is not working for calls to WebApi project. (http://server/application/api/...). In browser I receive correct reply, for example trying to call non-existent method results in: In debug output:

AuditModule: 2016-05-04 14:15:45,201 [56] INFO  AuditModule [(null)] - Request::begin
iisexpress.exe Information: 0 : Request, Method=GET, Url=http://localhost:52258/api/controller_name/fake_method, Message='http://localhost:52258/api/controller_name/fake_method'
iisexpress.exe Information: 0 : Message='controller_name', Operation=DefaultHttpControllerSelector.SelectController
iisexpress.exe Information: 0 : Message='Application.Controllers.controller_nameController', Operation=DefaultHttpControllerActivator.Create
iisexpress.exe Information: 0 : Message='Application.Controllers.controller_nameController', Operation=HttpControllerDescriptor.CreateController
iisexpress.exe Information: 0 : Message='Will use same 'JsonMediaTypeFormatter' formatter', Operation=JsonMediaTypeFormatter.GetPerRequestFormatterInstance
iisexpress.exe Information: 0 : Message='Selected formatter='JsonMediaTypeFormatter', content-type='application/json; charset=utf-8'', Operation=DefaultContentNegotiator.Negotiate
iisexpress.exe Warning: 0 : Message='UserMessage='No HTTP resource was found that matches the request URI 'http://localhost:52258/api/controller_name/fake_method'.', MessageDetail='No action was found on the controller 'controller_name' that matches the request.'', Operation=ApiControllerActionSelector.SelectAction, Status=404 (NotFound), Exception=System.Web.Http.HttpResponseException: Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for deta
   at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext)
   at System.Web.Http.Controllers.ApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext)
   at System.Web.Http.Tracing.Tracers.HttpActionSelectorTracer.<>c__DisplayClass2.<System.Web.Http.Controllers.IHttpActionSelector.SelectAction>b__0()
   at System.Web.Http.Tracing.ITraceWriterExtensions.TraceBeginEnd(ITraceWriter traceWriter, HttpRequestMessage request, String category, TraceLevel level, String operatorName, String operationName, Action`1 beginTrace, Action execute, Action`1 endTrace, Action`1 errorTrace)
iisexpress.exe Warning: 0 : Message='UserMessage='No HTTP resource was found that matches the request URI 'http://localhost:52258/api/controller_name/fake_method'.', MessageDetail='No action was found on the controller 'controller_name' that matches the request.'', Operation=controller_nameController.ExecuteAsync, Status=404 (NotFound), Exception=System.Web.Http.HttpResponseException: Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for deta
   at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext)
   at System.Web.Http.Controllers.ApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext)
   at System.Web.Http.Tracing.Tracers.HttpActionSelectorTracer.<>c__DisplayClass2.<System.Web.Http.Controllers.IHttpActionSelector.SelectAction>b__0()
   at System.Web.Http.Tracing.ITraceWriterExtensions.TraceBeginEnd(ITraceWriter traceWriter, HttpRequestMessage request, String category, TraceLevel level, String operatorName, String operationName, Action`1 beginTrace, Action execute, Action`1 endTrace, Action`1 errorTrace)
   at System.Web.Http.Tracing.Tracers.HttpActionSelectorTracer.System.Web.Http.Controllers.IHttpActionSelector.SelectAction(HttpControllerContext controllerContext)
   at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
   at System.Web.Http.Tracing.Tracers.HttpControllerTracer.<ExecuteAsyncCore>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__18`1.MoveNext()
iisexpress.exe Information: 0 : Response, Status=404 (NotFound), Method=GET, Url=http://localhost:52258/api/controller_name/fake_method, Message='Content-type='application/json; charset=utf-8', content-length=unknown'
iisexpress.exe Information: 0 : Operation=JsonMediaTypeFormatter.WriteToStreamAsync
iisexpress.exe Information: 0 : Operation=controller_nameController.Dispose
AuditModule: 2016-05-04 14:15:45,236 [56] INFO  AuditModule [(null)] - Request::end
The thread 0x6398 has exited with code 0 (0x0).

and in browser (with correct http response code, 404 in this case):

{
    "Message": "No HTTP resource was found that matches the request URI 'http://localhost:52258/api/controller_name/fake_method'.",
    "MessageDetail": "No action was found on the controller 'controller_name' that matches the request."
}

I tried:

  1. Same instance of IHttpModule implementation does not catch; it catches BeginRequest and EndRequest, but not exceptions (neither in-code, nor http)
  2. Exception filters and handlers do not catch whenever technique I'm using to register filters
  3. Replacing in services via config.Services.Replace()

The initial problem is coming from requirement to leave fine audit trail of everything happened in application -- all successful and failed requests that happened both in frontend and in API. I'm using Log4net to log these (and other) events.

Hence is my question -- can anyone suggest another way to register all exceptions (in-code, http) in this configuration or point out, where the problem might come from?

Thanks!

upd 1: I am able to catch in-code (when I throw an exception in a WebApi method) via standard: GlobalConfiguration.Configuration.Filters.Add(new CustomExceptionFilterAttribute());

upd 2: If I do the following:

public class MyExceptionFilter : IExceptionFilter {...}
GlobalConfiguration.Configuration.Filters.Add(new MyExceptionFilter ());

Then MyExceptionFilter is not called. However, impl of ExceptionFilterAttribute gets called.

Then what I'm left with is how to catch http (wrong path into WebApi) and framework (unparseable parameters and so) errors and exceptions.

upd3: Solved. Here is steps of my solution. Unfortunately, solution is a bit fragile and requires certain sequence of init steps, not too error prone:

  1. In Application_Start, before registering any routes:

    GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new AuditExceptionLogger());

  2. Register Frontend routes

  3. Register WebApi routes
  4. Register 404 handler and create Error controller

    RouteTable.Routes.MapHttpRoute( name: "Error404", routeTemplate: "{*url}", defaults: new { controller = "Error", action = "Handle404" } );

  5. Implement DefaultHttpControllerSelector and ApiControllerActionSelector, catching exceptions there and replace by

    GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), new HttpNotFoundAwareDefaultHttpControllerSelector(GlobalConfiguration.Configuration)); GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpActionSelector), new HttpNotFoundAwareControllerActionSelector());

And I have not yet designed CustomError handling -- I want to show proper error page to invalid Frontend requests

Community
  • 1
  • 1
rezdm
  • 165
  • 1
  • 11
  • Have you taken a gander at [Global Error Handling in ASP.NET Web API 2](http://www.asp.net/web-api/overview/error-handling/web-api-global-error-handling)? – Will Ray May 04 '16 at 15:45
  • Yes, I did. It turned out (see my last upd) that sequence of init is quite important. In the end I do have all exceptions and errors handled properly, it's just not error-prone as I'd like it to be. – rezdm May 04 '16 at 15:52
  • Ahh, I see. I would recommend posting your solution as an answer to the question (and marking it as the answer) for others. – Will Ray May 04 '16 at 15:54
  • I'll do, for sure. I'm just not yet convinced if this is really the best approach. – rezdm May 04 '16 at 15:57

1 Answers1

0

Could you use an external service to catch the Exceptions, such as Exceptionless?

https://exceptionless.com/

illug
  • 793
  • 1
  • 9
  • 23