2

I'm using Unity with my WebApi project, and registering types using the config file. I've been injecting dependencies into controller constructors and it has been working beautifully... until I forgot a type mapping and received the following error:

An error occurred when trying to create a controller of type 'FooController'. Make sure that the controller has a parameterless public constructor.

The problem isn't that I received the error. I can easily add the mapping to resolve it. The problem is that internals were shared with the client and the client received a HTTP Status Code of 200.

I read a similar article (Unity wraps exception to the ResolutionFailedException. How to avoid?), but those answers were not helpful; stating there is no solution or seeming off topic. It is difficult for me to accept that there is no solution, so I thought I might rephrase the question in hopes that it will receive a better answer.

How can I catch/intercept the ResolutionFailedException so that a Http Error 500 is returned instead? Is there an event I can listen for or a class I can wrap in some way? For example, can I wrap the DefaultHttpControllerActivator and use my wrapper to catch an exception and return it as a HTTP 500 error? I would expect a HTTP 500 to be filtered from the client based on the CustomErrors configuration setting. Also, I could log the error to my log file instead of returning details to the client.

Here's a example of the error as XML:

<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
An error occurred when trying to create a controller of type 'FooController'. Make sure that the controller has a parameterless public constructor.
</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace>
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) 
at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request) 
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()
</StackTrace>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Resolution of the dependency failed, type = "MyWebApi.API.Controllers.FooController", name = "(none)". Exception occurred while: while resolving. Exception is: InvalidOperationException - The current type, MyWebApi.DataProvider.Interfaces.IFooProvider, is an interface and cannot be constructed. Are you missing a type mapping? ----------------------------------------------- 
At the time of the exception, the container was: Resolving MyWebApi.API.Controllers.FooController,(none) Resolving parameter "FooProvider" of constructor MyWebApi.API.Controllers.FooController(MyWebApi.DataProvider.Interfaces.IFooProvider FooProvider) Resolving MyWebApi.DataProvider.Interfaces.IFooProvider,(none)
</ExceptionMessage>
<ExceptionType>
Microsoft.Practices.Unity.ResolutionFailedException
</ExceptionType>
<StackTrace>
at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides) 
at Microsoft.Practices.Unity.UnityContainer.Resolve(Type t, String name, ResolverOverride[] resolverOverrides) 
at Microsoft.Practices.Unity.UnityContainerExtensions.Resolve(IUnityContainer container, Type t, ResolverOverride[] overrides) 
at Microsoft.Practices.Unity.WebApi.UnityDependencyResolver.SharedDependencyScope.GetService(Type serviceType) 
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator) 
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
</StackTrace>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>
The current type, MyWebApi.DataProvider.Interfaces.IFooProvider, is an interface and cannot be constructed. Are you missing a type mapping?
</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace>
at Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy.ThrowForAttemptingToConstructInterface(IBuilderContext context) 
at lambda_method(Closure , IBuilderContext ) 
at Microsoft.Practices.ObjectBuilder2.DynamicBuildPlanGenerationContext.<>c__DisplayClass1.<GetBuildMethod>b__0(IBuilderContext context) 
at Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlan.BuildUp(IBuilderContext context) 
at Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context) 
at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context) 
at Microsoft.Practices.ObjectBuilder2.BuilderContext.NewBuildUp(NamedTypeBuildKey newBuildKey) 
at Microsoft.Practices.Unity.ObjectBuilder.NamedTypeDependencyResolverPolicy.Resolve(IBuilderContext context) 
at lambda_method(Closure , IBuilderContext ) 
at Microsoft.Practices.ObjectBuilder2.DynamicBuildPlanGenerationContext.<>c__DisplayClass1.<GetBuildMethod>b__0(IBuilderContext context) 
at Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlan.BuildUp(IBuilderContext context) 
at Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context) 
at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context) 
at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides)
</StackTrace>
</InnerException>
</InnerException>
</Error>
KrimblKrum
  • 183
  • 3
  • 11
  • Are you able to trap it in Global `Application_Error()`? – Jasen Oct 11 '17 at 18:57
  • No, I'm not. It seems that the error is being caught by something, and then packaged as a HTTP 200 response with an XML representation of the error in the body. I don't know what that "something" is though. – KrimblKrum Oct 12 '17 at 20:41

1 Answers1

2

I found the answer here: catch all unhandled exceptions in ASP.NET Web Api

I learned the following things when implementing my preferred solution:

  • The full error w/ stack trace is only visible because of the CustomErrors setting. I forgot that I was running everything locally and CustomErrors=RemoteOnly.
  • If I only want to catch the exceptions and log them, then add an IExceptionLogger. This is the solution I implemented for my current needs.
  • If I want to catch the exception and manipulate the response, then replace IExceptionHandler. This is good to know.
  • Neither solution captures ALL exceptions. I'm still using Application_Error to capture exceptions that aren't caught within my ApiController methods.
KrimblKrum
  • 183
  • 3
  • 11