I'm trying to add a custom IHttpActionInvoker to my WebAPI application in order to prevent the need for lots of repeated exception handling code in my action methods.
There really doesn't seem to be much out there about how to do this other than this article. After writing my IHttpActionInvoker as per the article I added this code:
GlobalConfiguration.Configuration.Services.Remove(typeof(IHttpActionInvoker),
GlobalConfiguration.Configuration.Services.GetActionInvoker());
GlobalConfiguration.Configuration.Services.Add(typeof(IHttpActionInvoker),
new MyApiControllerActionInvoker());
Into a method within my Global.asax file. Now when executing a call to my API I get the following exception raised at the Remove() method:
The service type IHttpActionInvoker is not supported
I guess I have two questions.
Considering there doesn't seen to be an awful lot out there about writing custom IHttpActionInvoker classes is this considered a good approach to solve exception handling in WebAPI applications?
Does anyone know why I would get such an exception when executing the Remove() method and how best to fix this particular issue?
I suffered the same error you describe when attempting to remove the service.
I discovered I didn't need to remove anything from the global config, as it appears if you've registered the interface in your container then it will resolve this first.
For example, I'm using SimpleInjector and in my global.asax I have this:
container.Register<IHttpActionInvoker , MyApiControllerActionInvoker >();
// Register the dependency resolver.
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
At runtime, it is resolving MyApiControllerActionInvoker dependency when required.
You can then perform exception handling in your customer ActionInvoker and any dependencies set in your constructor will be wired up correctly. The reason I was looking at the ActionInvoker was to get the constructor injection, since injecting into Attributes appears to require property injection.
Also rather than the remove/insert, replace seems to work. (in Global.asax)
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpActionInvoker), new MyApiControllerActionInvoker(fooService));
Have you considered registering an exception filter instead? Here's some documentation about that:
http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling
You shouldn't have to fall down to the action invoker layer if all you want to do is handle some exceptions in a particular way.
As for me it works with IActionInvoker instead of IHttpActionInvoker. As I understand, IHttpActionInvoker is used for the async api calls, isn't it?
public class RepControllerActionInvoker : ControllerActionInvoker
{
ILogger _log;
public RepControllerActionInvoker()
: base()
{
_log = DependencyResolver.Current.GetService<ILogger>();
}
public override bool InvokeAction(ControllerContext controllerContext, string actionName)
{
try
{
return base.InvokeAction(controllerContext, actionName);
}
catch (Exception e)
{
_log.Error(e);
throw new HttpException(500, "Internal error");
}
}
}
Related
I'm having trouble using DryIoc for constructor injection into a ViewModel using Prism with Xamarin. I am using the Nuget package Prism.DryIoc.Forms.
In my project I get the following error in AuthenticatePage.xaml.g.cs
Unable to resolve Object {RequiredServiceType=Project.ViewModels.AuthenticatePageViewModel} with 1 arg(s)
in wrapper Func<Xamarin.Forms.Page, Object> {RequiredServiceType=Project.ViewModels.AuthenticatePageViewModel} with 1 arg(s)
from container
with normal and dynamic registrations:
MainPage, {ID=44, ImplType=Project.Views.MainPage}}
NavigationPage, {ID=43, ImplType=Xamarin.Forms.NavigationPage}}
AuthenticatePage, {ID=45, ImplType=Project.Views.AuthenticatePage}}
Specifically, it points to the line
private void InitializeComponent() {
global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(AuthenticatePage));
}
Of note is that if I call the following in App.OnInitialized, the object resolves fine:
c.Register<INegotiator, Negotiator>(Reuse.Singleton);
var n = c.Resolve<INegotiator>();
n.ResumeSessionAsync(); // This works fine, no problems.
await NavigationService.NavigateAsync("NavigationPage/AuthenticatePage"); // Error thrown here
If I remove the constructor injection from my ViewModel it works fine (Aside from keeping the default navigationService injection, which works fine). Even trying to inject a basic class like ILogger (no dependencies) fails.
public AuthenticatePageViewModel(INavigationService navigationService, ILogger logger) : base (navigationService)
{
Title = "Authentication Page...";
}
I'm going to keep investigating, but is it obvious to someone here if I'm fundamentally doing something wrong? If I had to guess I would say it's to do with a conflict with Prisms built in Ioc container and DryIoc?
Edit:
I'm using the latest version of Prism.DryIoc.Forms available on NuGet (7.0.0.396) which says it includes DryIoc 2.12.26. I have so far simply followed the template available for Visual Studio which lists setting up navigation as follows:
protected override async void OnInitialized()
{
InitializeComponent();
var c = new Container();
c.Register<ILogger, LoggerConsole>(Reuse.Singleton);
c.RegisterMany(new[] { Assembly.Load("Project.UWP") },
serviceTypeCondition: type => type == typeof (ILocalFileHandler));
c.Register<INegotiator, Negotiator>(Reuse.Singleton);
// var n = c.Resolve<INegotiator>();
// n.ResumeSessionAsync(); // <- This will run fine. Negotiator class has ILogger and ILocalFileHandler injected into it.
await NavigationService.NavigateAsync("NavigationPage/AuthenticatePage");
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<MainPage>();
containerRegistry.RegisterForNavigation<AuthenticatePage>();
}
I can't find any info online on if/how I should be using Prism.DryIoc.DryIocContainerExtensions to set up navigation? Even modifying the sample app to include basic construction injection results in the error "Value Cannot Be Null" in the same xaml.g.cs file?
Prism 7.0 and below allows the exception to bubble up, in order to diagnose the root cause of your issue you want to better diagnose this issue I suggest you do a little try/catch to see what and where the error really is.
protected override void OnInitialized()
{
try
{
// Check if there is an initialization exception
var page = new AuthenticationPage();
// Validate that the page resolves ok
var page2 = Container.Resolve<object>("AuthenticationPage");
// Validate that your ILogger interface is registered and resolves ok
var logger = Container.Resolve<ILogger>();
// Check for Registration/initialization exceptions
var vm = Container.Resolve<AuthenticationPageViewModel>();
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
System.Diagnostics.Debugger.Break();
}
}
You haven't specified at what point you're getting this error, though typically with XAML Compilation enabled you would see exceptions in the {pageName}.xaml.g.cs during compilation and not runtime. Either way, given that your exception is coming from the generated XAML code behind class, this tells me it is most likely a problem with your XAML. A very simple way to validate this is to remove all of the XAML content in your AuthenticationPage so that you have an empty page.
Given the code you've provided as part of your question, I would say you have no registration for your ILogger interface which would likely throw an exception causing the problem you're seeing. Regardless of what/where the error is, the try/catch shown above would be the easiest way to determine the root cause.
Following #Dan S.'s diagnoses suggestion as well as reading this article (http://brianlagunas.com/whats-new-in-prism-for-xamarin-forms-7-0/) I realized that I should have been using the Prism.Ioc.ContainerRegistry abstraction layer to interface with DryIoc. Prior to this I had been working directly with DryIoc's classes.
Once I modified my registration code to use Prism.Ioc.IContainerRegistry it worked perfectly.
protected override void RegisterTypes(IContainerRegistry cr)
{
cr.Register<ILogger, LoggerConsole>();
cr.GetContainer().RegisterMany(new[] { Assembly.Load("Project.UWP") },
serviceTypeCondition: type => type == typeof(ILocalFileHandler));
cr.Register<INegotiator, Negotiator>();
cr.RegisterForNavigation<NavigationPage>();
cr.RegisterForNavigation<MainPage>();
cr.RegisterForNavigation<AuthenticatePage>();
}
Many of our current controllers look like this:
[HttpPost]
public List<Foo> Post([FromBody]Bar model)
{
if (model == null)
{
throw new ArgumentNullException();
}
try
{
// business logic
}
catch (Exception ex)
{
// logging
}
return dto;
}
A lot of code is being repeated here though. What I'd like to do is implement a base controller that handles exceptions so I can return a standardized response with fields like Payload, Success, Error, etc.
Prior to .net core this was possible by providing an override of OnException however this doesn't appear to work with a .net core api controller. How do I go about consolidating this exception logic to return a custom response when things go awry in my controller bodies?
I'd like this, as a starting point:
[HttpPost]
public StandardFoo Post([FromBody]Bar model)
{
if (model == null)
{
throw new ArgumentNullException();
}
// business logic
return new StandardFoo(){Payload: dto};
}
Where exceptions thrown by model validation or business logic bubble up to some piece of logic that returns a new StandardFoo with a property containing the exception details.
If shortly, you should not catch and process exceptions in your controllers.
Instead, you need to separate normal and error flows in your code and then process error flow separately. One of the main approaches to indicate that normal flow is not possible is to raise the .NET Exceptions (and you use it). But:
Controllers actions should be aware only of normal flow. No try-catch logic and so on.
For input validation use ActionFilter. You may have global filters for all controllers or define specific per action. See Filters section in documentation. ASP.NET Core allows also do Model Validation.
During controller action execution you should raise exceptions as soon as possible and stop further pipeline execution. And yes, the Exception may be raised on any of the levels (action level, Service/Business layer, DA layer, etc).
How to handle the raised exception then?
use provided by ASP.NET Core error handling approaches (like ExceptionHandler, or Exception Filters), it allows to analyze exceptions and generate appropriate/different responses accordingly. Look into related SO Error handling in ASP.NET Core question for the example. There is also the error-handling section in documentation.
I would recommend creating a custom action filter. This can be wrapped around every incoming request in the WebApiConfig Register method(See below).
In my example, I am checking that the model state is valid.
If it's not, I create an ErrorResponse and send back a Bad Request.
You don't have to simply send back the model state like in the example below, you could return anything you actually want.
This way it becomes uniform across all endpoints that have a model that needs to be validated as well as any other checks you want to do at this point in the pipeline.
Note: Because we are registering this attribute globally we dont then have to declare it anywhere else, from this point on, all incoming traffic be inspected by this class.
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
public override bool AllowMultiple
{
get { return false; }
}
}
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.Filters.Add(new ValidateModelAttribute());
}
}
If you're using .Net Core, a great way to handle errors is to use the Exception Handling Middleware.
See these articles for more details:
https://code-maze.com/global-error-handling-aspnetcore/
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-5.0
This enables the removing of error handling logic, which is a cross cutting concern, into a dedicated component, allowing your controllers to remain thin and have a single responsibility - ultimately making your code/application more maintainable and robust.
I have a setup that looks like this:
Controller > IService > IUnitOfWork > IContext > IConnectionStringHelper
In almost all cases I want IConnectionStringHelper to be resolved by ConnectionStringHelper, but when i use ISpecialService, I want to use SpecialConnectionStringHelper as the resolver.
I use a IExceptionFilter to log and redirect the user by implementing the OnException, and in this method I resolve ISpecialService. This is a hook to catch all unhandled exceptions that might occur.
What happens is that I call a controller and that controller gets everything injected as it should. If an error occures, the OnException method gets called and in this method i try to resolve my SpecialService
var spc = WindsorConfig._windsorContainer.Resolve<ISpecialService>();
int? errandId = spc.CreateErrandFromError(message, innerMessage, StackTrace);
The problem I'm facing is that when I now resolve the ISpecialService, the chain is already resolved but with the wrong implementation of IConnectionStringHelper. I have tried to use the DependsOn but I'm not sure how to make the IConnectionStringHelper depend on IService (ISpecialService). I have tried some variations and latest I tried was this
Component.For<IConnectionStringHelper>().ImplementedBy<ConnectionStringHelper>().LifeStyle.PerWebRequest,
Component.For<IConnectionStringHelper>().ImplementedBy<SpecialConnectionStringHelper>().LifeStyle.Transient,
Component.For<ISpecialService>().ImplementedBy<SpecialService>().LifeStyle.Transient
.DependsOn(Dependency.OnComponent<IUnitOfWork, UnitOfWork>())
.DependsOn(Dependency.OnComponent<IContext, Context>())
.DependsOn(Dependency.OnComponent<IConnectionStringHelper, SpecialConnectionStringHelper>())
As the ISpecialService doesn't have the IConnectionStringHelper as a directly injected interface, I can see how this setup might not work, but is it possible, and how can I achieve it?
i have external controller (for e.g. ExtController ) in another assembly ( folder config/extensions ).
Registration:
builder.RegisterControllers(assembly).Named<IController>(t =>
t.Name.Replace("Controller", string.Empty)
);
Getting a controller ( i have own controller factory ):
public override IController CreateController
(System.Web.Routing.RequestContext requestContext, string controllerName)
{
try
{
var ctrl = _base.CreateController(requestContext, controllerName);
return ctrl;
}
catch (HttpException htte)
{
Object ic = null;
if (_container.TryResolveNamed(controllerName, typeof(IController), out ic))
{
return ic as IController;
}
else
throw htte;
}
}
And if i doing request for this controller i get "root" autofac lifetime scope.
In other controllers i got "AutofacWebrequest" scope.
Could you help me ? Maybe is another way for controller creation from another assembly ?
Edit
I resolved my problem but i think is not the best way i can do it.
I changed from:
if (_container.TryResolveNamed(controllerName, typeof(IController), out ic))
to:
if ( (DependencyResolver.Current as Autofac.Integration.Mvc.AutofacDependencyResolver).RequestLifetimeScope.TryResolveNamed(controllerName, typeof(IController), out ic))
Unfortunately, if your integration point is a custom controller factory working against named services, you are probably stuck with what you have. You can use AutofacDependencyResolver.Current rather than casting DependencyResolver.Current yourself, but the principle will still be the same.
However, I did notice you didn't show what the base ControllerFactory is. It appears you have a sort of decorator pattern going with _base being called rather than base (without the underscore). The DefaultControllerFactory already runs controller instantiation through the dependency resolver. Of course, that would mean you need to not register controllers as named services and instead just register them with the standard RegisterControllers method.
Again, if you have to register them named (for whatever reason) and/or if the base call to CreateController isn't going through the standard DefaultControllerFactory, then what you have is correct. If you can stop registering controllers as named services, though, the standard dependency resolution pipeline will "just work" and you won't need all the extra code.
There is detailed documentation on Autofac MVC integration on the Autofac doc site.
I am using Unity.Wcf to inject dependencies in the service class and it work fine. But if I configure my dependencies incorrectly an exception is thrown by unity that it cannot build up my service instance which is correct. Does anyone knows where I can handle this exception to log it for example? It's much easier to check the log than debug it every time.
Another way to capture the exception is to override the CreateServiceHost methods in the service factory. Call base.CreateServiceHost() inside of a try...catch block and use your logging component of choice to save the exception to the log. Other than logging the error, the behavior will be the same.
public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
{
try {
return base.CreateServiceHost(constructorString, baseAddresses);
}
catch (Exception ex) {
// log here
throw;
}
}
After some investigations I have found out how the instance is created and it seems there is no way to add logging to it easily. The only way it to either change the source code of Unity.Wcf or to inherit few classes and override default behavior. For those who are interested the logic is following: UnityServiceHostFactory creates UnityServiceHost, UnityServiceHost adds behavior class UnityInstanceProvider, this UnityInstanceProvider has a GetInstance method that creates the service and does Unity resolving. So to log something you need to do one of the following:
Substitute this class with yours and inherit both UnityServiceHostFactory and UnityServiceHost
Get the sources and change something right in this class (UnityInstanceProvider).