Exception Filters MVC - Dependency Injection - asp.net-mvc

I am not getting logger instance in OnException method. I am using IunityContainer and have initialized in Global.asax
Please help me to correct the code.
Thanks
public class InjectIntoActionInvoker : ControllerActionInvoker
{
private IUnityContainer _container;
public InjectIntoActionInvoker(IUnityContainer container)
{
_container = container;
}
protected override ActionExecutedContext InvokeActionMethodWithFilters(
ControllerContext controllerContext,
IList<IActionFilter> filters,
ActionDescriptor actionDescriptor,
IDictionary<string, object> parameters)
{
foreach (IActionFilter filter in filters)
{
_container.BuildUp(filter.GetType(), filter);
}
return base.InvokeActionMethodWithFilters(controllerContext, filters, actionDescriptor, parameters);
}
}
private ILogger logger;
[Dependency]
public ILogger _logger
{
get
{
return this.logger;
}
set
{
if (value != null)
{
this.logger = value;
}
}
}
public void OnException(ExceptionContext filterContext)
{
if (filterContext != null && filterContext.Exception!=null)
{
_logger.LogError(filterContext.Exception.Message, filterContext.Exception);
}
}
}
public static class Bootstrapper
{
public static IUnityContainer Initialise()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
}
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterInstance<ILogger>(new Logger());
container.RegisterType<IActionInvoker, InjectIntoActionInvoker>();
}
}

Try injecting the logger in constructor...
public InjectIntoActionInvoker(IUnityContainer container, ILogger lo)
{
_container = container;
this._logger = lo;
}

I'm really not convinced this is going to work for you without using a controller factory. I'm stealing the code example from here, but this gives you a good idea on how to create a customer controller factory, which also configures a custom action invoker:
public class MyControllerFactory : DefaultControllerFactory
{
public override IController CreateController(RequestContext context, string controllerName)
{
var controller = base.CreateController(context, controllerName);
return ReplaceActionInvoker(controller);
}
private IController ReplaceActionInvoker(IController controller)
{
var mvcController = controller as Controller;
if (mvcController != null)
mvcController.ActionInvoker = new ControllerActionInvokerWithDefaultJsonResult();
return controller;
}
}
public class ControllerActionInvokerWithDefaultJsonResult : ControllerActionInvoker
{
public const string JsonContentType = "application/json";
protected override ActionResult CreateActionResult(ControllerContext controllerContext, ActionDescriptor actionDescriptor, object actionReturnValue)
{
if (actionReturnValue == null)
return new EmptyResult();
return (actionReturnValue as ActionResult) ?? new ContentResult()
{
ContentType = JsonContentType,
Content = JsonConvert.SerializeObject(actionReturnValue)
};
}
}
You could still have the custom action invoker, and controller factory for that matter, in a different assembly if you want.

Related

Changing the type of action parameter at runtime depending on current user in aspnet webapi

How to alter the TViewModel from within a action filter or a model binder?
[HasPriviliege]
public IHttpActionResult Get(long id)
{
var entity = AutoMapper.Mapper.Map<TViewModel, TEntity>(model);
repo.Update(id, entity);
repo.Save();
return Ok(model);
}
[HasPriviliege]
public IHttpActionResult Edit(long id, TViewModel model)
{
var entity = AutoMapper.Mapper.Map<TViewModel, TEntity>(model);
repo.Update(id, entity);
repo.Save();
return Ok(model);
}
the filter should be
public class HasPriviliege:ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if(getPrivileges()=="doctor"){
//the TViewModel(view model type to bind to) should be
// DoctorPatientViewModel should be;
}else{
//the TViewModel(view model type to bind to) should be
//ExaminationPatientViewModel
}
//base.OnActionExecuting(actionContext);
}
}
or alternativaly, the model binder
public class IPrivilegeableModelBinder: IModelBinder
{
public object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
//return (hasPriviliege()?DoctorPatientViewModel:ExaminationPatientViewModel) ;
}
}
Rather than write an over-bloated comment, I'll post my suggestion on how we accomplished something similar to this using a generic controller.
Controller factory:
public class ControllerFactory : IControllerFactory
{
public IController CreateController(RequestContext requestContext, string controllerName)
{
Type controllerType = typeof(GenericController<>);
Type genericType = controllerType.MakeGenericType(GetPrivilegeType());
ConstructorInfo ctor = genericType.GetConstructor(new Type[]{});
return (IController)ctor.Invoke(new object[] { });
}
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
...
return SessionStateBehavior.ReadOnly;
}
public void ReleaseController(IController controller)
{
if (controller is IDisposable)
{
((IDisposable)controller).Dispose();
}
}
private string GetPrivilegeType()
{
if (getPrivileges() == "doctor") {
return typeof(DoctorPatientViewModel);
} else {
return typeof(ExaminationPatientViewModel);
}
}
}
Register it like this:
ControllerBuilder.Current.SetControllerFactory(new ControllerFactory());
...and finally what your controller might look like
public class GenericController<TViewModel> // TViewModel will be the privilege type from the factory
where TViewModel : IPrivilege
{
[HasPriviliege]
public IHttpActionResult Edit(long id, TViewModel model)
{
var entity = AutoMapper.Mapper.Map<TViewModel, TEntity>(model);
repo.Update(id, entity);
repo.Save();
return Ok(model);
}
}
That's the most basic example to get a generic controller working for mvc which might go some way to what you're trying to accomplish.

How to set up Windsor for Sitecore MVC Web Api Controller

I have implemented a Windsor for my controller like described here
http://sitecore-estate.nl/wp/2014/12/sitecore-mvc-dependency-injection-using-castle-windsor/
and set up my WebApi like here https://kb.sitecore.net/en/Articles/2015/07/15/11/30/700677.aspx
for regular controller it is works good. But I wonder how to use it for ApiController. Next way is not working
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly().BasedOn<IHttpController>().LifestyleTransient());
}
Yes answer for this question would be like use IHttpControllerActivator:
public class WindsorHttpControllerFactory : IHttpControllerActivator
{
private readonly IWindsorContainer _container;
public WindsorHttpControllerFactory(IWindsorContainer container)
{
_container = container;
}
public IHttpController Create(HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller =
(IHttpController)_container.Resolve(controllerType);
request.RegisterForDispose(
new Release(
() => _container.Release(controller)));
return controller;
}
class Release : IDisposable
{
readonly Action _release;
public Release(Action release)
{
_release = release;
}
public void Dispose()
{
_release();
}
}
}
public class WebApiInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Types.FromThisAssembly().BasedOn<IHttpController>().LifestyleTransient());
}
}
public class InitializeWindsorControllerFactory
{
public virtual void Process(PipelineArgs args)
{
SetupControllerFactory(args);
}
public virtual void SetupControllerFactory(PipelineArgs args)
{
IWindsorContainer container = new WindsorContainer().Install(FromAssembly.This());
IControllerFactory controllerFactory = new WindsorControllerFactory(container.Kernel);
SitecoreControllerFactory sitecoreControllerFactory = new SitecoreControllerFactory(controllerFactory);
System.Web.Mvc.ControllerBuilder.Current.SetControllerFactory(sitecoreControllerFactory);
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator),new WindsorHttpControllerFactory(container));
}
}
and config settings for
<pipelines>
<initialize>
<processor type="My.IoC.InitializeWindsorControllerFactory, My.IoC" patch:instead="*[type='Sitecore.Mvc.Pipelines.Loader.InitializeControllerFactory, Sitecore.Mvc']"/>
</initialize>
</pipelines>

Making model binding work for a model with no default constructor

I’ve been trying to figure a way to have a model-binding go on with a model with a constructor with arguments.
the action:
[HttpPost]
public ActionResult Create(Company company, HttpPostedFileBase logo)
{
company.LogoFileName = SaveCompanyLogoImage(logo);
var newCompany = _companyProvider.Create(company);
return View("Index",newCompany);
}
and the model
public Company(CustomProfile customProfile)
{
DateCreated = DateTime.Now;
CustomProfile = customProfile;
}
I've done my research and seems I need to mess around with my ninjectControllerfactory:
public class NinjectControllerFactory : DefaultControllerFactory
{
private readonly IKernel ninjectKernel;
public NinjectControllerFactory()
{
ninjectKernel = new StandardKernel();
AddBindings();
}
protected override IController GetControllerInstance(RequestContext requestContext,
Type controllerType)
{
return controllerType == null
? null
: (IController) ninjectKernel.Get(controllerType);
}
private void AddBindings()
{
ninjectKernel.Bind<IAuthProvider>().To<FormsAuthProvider>();
ninjectKernel.Bind<IMembershipProvider>().To<MembershipProvider>();
ninjectKernel.Bind<ICustomProfileProvider>().To<CustomProfileProvider>();
ninjectKernel.Bind<ICompanyProvider>().To<CompanyProvider>();
}
}
I also feel I need to modify my model binder but I'm not clear on the way forward:
public class CustomProfileModelBinder : IModelBinder
{
private const string sessionKey = "CustomProfile";
#region IModelBinder Members
public object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
// get the Cart from the session
var customProfile = (CustomProfile) controllerContext.HttpContext.Session[sessionKey];
// create the Cart if there wasn't one in the session data
if (customProfile == null)
{
customProfile = new CustomProfile("default name");
controllerContext.HttpContext.Session[sessionKey] = customProfile;
}
// return the cart
return customProfile;
}
#endregion
}
Hope this explains my issue, I'm sorry if its a rather long winded question!
Thanks for any assistance
In this case it seems that the parameter you need to create (CustomProfile) must be taken from the session. You could then use a specific model binder for the Company model that derives from the default model binder, changing only the way it creates an instance of the Company class (it will then populate the properties in the same way as the default one):
public class CompanyModelBinder: DefaultModelBinder
{
private const string sessionKey = "CustomProfile";
protected override object CreateModel(ControllerContext controllerContext,
ModelBindingContext bindingContext,
Type modelType)
{
if(modelType == typeOf(Company))
{
var customProfile = (CustomProfile) controllerContext.HttpContext.Session[sessionKey];
// create the Cart if there wasn't one in the session data
if (customProfile == null)
{
customProfile = new CustomProfile("default name");
controllerContext.HttpContext.Session[sessionKey] = customProfile;
}
return new Company(customProfile);
}
else
{
//just in case this gets registered for any other type
return base.CreateModel(controllerContext, bindingContext, modelType)
}
}
}
You will register this binder only for the Company type by adding this to the global.asax Application_Start method:
ModelBinders.Binders.Add(typeOf(Company), CompanyModelBinder);
Another option could be to create a dependency-aware model binder using the Ninject dependencies by inheriting from the DefaultModelBinder (As you are using Ninject, it knows how to build instances of concrete types without the need of registering them).
However you would need to configure a custom method that builds the CustomProfile in Ninject, which I believe you could do using the ToMethod().
For this you would extract you would extract your configuration of your Ninject kernel outside the controller factory:
public static class NinjectBootStrapper{
public static IKernel GetKernel()
{
IKernel ninjectKernel = new StandardKernel();
AddBindings(ninjectKernel);
}
private void AddBindings(IKernel ninjectKernel)
{
ninjectKernel.Bind<IAuthProvider>().To<FormsAuthProvider>();
ninjectKernel.Bind<IMembershipProvider>().To<MembershipProvider>();
ninjectKernel.Bind<ICustomProfileProvider>().To<CustomProfileProvider>();
ninjectKernel.Bind<ICompanyProvider>().To<CompanyProvider>();
ninjectKernel.Bind<CustomProfile>().ToMethod(context => /*try to get here the current session and the custom profile, or build a new instance */ );
}
}
public class NinjectControllerFactory : DefaultControllerFactory
{
private readonly IKernel ninjectKernel;
public NinjectControllerFactory(IKernel kernel)
{
ninjectKernel = kernel;
}
protected override IController GetControllerInstance(RequestContext requestContext,
Type controllerType)
{
return controllerType == null
? null
: (IController) ninjectKernel.Get(controllerType);
}
}
In that case you would create this model binder:
public class NinjectModelBinder: DefaultModelBinder
{
private readonly IKernel ninjectKernel;
public NinjectModelBinder(IKernel kernel)
{
ninjectKernel = kernel;
}
protected override object CreateModel(ControllerContext controllerContext,
ModelBindingContext bindingContext,
Type modelType)
{
return ninjectKernel.Get(modelType) ?? base.CreateModel(controllerContext, bindingContext, modelType)
}
}
And you would update the global.asax as:
IKernel kernel = NinjectBootStrapper.GetKernel();
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory(kernel));
ModelBinders.Binders.DefaultBinder = new NinjectModelBinder(kernel);

ASP.NET MVC - setting custom IControllerFactory has no effect

I really dont understand what I'm doing wrong here - for some reason the IControllerFactory i register is not being used, and I end up with a System.ArgumentException:
Type 'Company.WebApi.Controllers.AController' does not have a default
constructor
at System.Linq.Expressions.Expression.New(Type type) at
System.Web.Http.Internal.TypeActivator.Create[TBase](Type
instanceType) 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)
In Global.asax.cs
protected void Application_Start()
{
//DI-setup
var container = new WindsorContainer().Install(new WebWindsorInstaller());
//set custom controller factory
var controllerFactory = container.Resolve<IControllerFactory>();
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
//register cors
container.Resolve<ICorsConfig>().RegisterCors(GlobalConfiguration.Configuration);
//routes
RegisterRoutes(RouteTable.Routes);
}
My IControllerFactory is based on this article http://keyvan.io/custom-controller-factory-in-asp-net-mvc and is implemented as following
public class ControllerFactory : IControllerFactory
{
private readonly IWindsorContainer _container;
public ControllerFactory(IWindsorContainer container)
{
if(container == null)
throw new ArgumentNullException("container");
_container = container;
}
public IController CreateController(RequestContext requestContext, string controllerName)
{
if (string.IsNullOrEmpty(controllerName))
throw new ArgumentNullException("controllerName");
var componentName = GetComponentNameFromControllerName(controllerName);
return _container.Resolve<IController>(componentName);
}
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
throw new NotImplementedException();
}
public void ReleaseController(IController controller)
{
_container.Release(controller);
}
static string GetComponentNameFromControllerName(string controllerName)
{
var controllerNamespace = typeof (CrudController<>).Namespace;
return string.Format("{0}.{1}Controller", controllerNamespace, controllerName);
}
}
I've been staring at this for hours, and really can't see why this isn't working. When debugging the ControllerFactory is never hit in any method except the constructor. Anyone see whats wrong or missing here?
(Setting ASP.NET MVC ControllerFactory has no effect does not answer my question)
Edit 1 - Controller
public class AController : CrudController<AModel>
{
public AController(IAHandler aHandler) : base(aHandler)
{
...
}
[HttpGet]
public HttpResponseMessage GetByUser(string aId)
{
...
}
}
public abstract class CrudController<T> : ApiController
where T : IModel, new()
{
protected CrudController(ICrudHandler<T> handler)
{
...
}
[HttpGet]
public HttpResponseMessage Get(string id)
{
...
}
[HttpPost]
public HttpResponseMessage Post(T input)
{
...
}
[HttpPut]
public HttpResponseMessage Put(T input)
{
...
}
}
Edit 2 - Installer
public sealed class WebWindsorInstaller : IWindsorInstaller
{
private bool _installComplete;
public void Install(IWindsorContainer container, IConfigurationStore store)
{
if(_installComplete)
return;
//register self for reuse
container.Register(Component.For<IWindsorInstaller>().Instance(this));
//controller factory
container.Register(
Component.For<IControllerFactory>().ImplementedBy<ControllerFactory>().LifeStyle.Singleton);
//Handlers
container.Register(
Classes.FromAssemblyContaining<AHandler>().InSameNamespaceAs<AHandler>().WithService.
DefaultInterfaces());
//Models
container.Register(
Classes.FromAssemblyContaining<AModel>().InSameNamespaceAs<AModel>().WithService.Self());
//DI
container.Register(Component.For<IWindsorContainer>().Instance(container));
container.Register(Component.For<IDependencyResolver>().ImplementedBy<WindsorDependencyResolver>());
//Controllers
container.Register(Classes
.FromAssemblyContaining<AController>()
.BasedOn<IHttpController>()
.LifestyleScoped());
//repositories
container.Register(
Classes.FromAssemblyContaining<ARepository>().InSameNamespaceAs<ARepository>().WithService.
DefaultInterfaces());
//cors
container.Register(Component.For<ICorsConfig>().ImplementedBy<CorsConfig>());
_installComplete = true;
}
}
From the error messages in your question it looks like you actually want to register WebAPI controllers. It's important to note that these are not the same as MVC controllers, and do not use the same controller factory (which you are trying to use).
See Mark Seeman's post Dependency Injection in ASP.NET Web API with Castle Windsor for details of how to do this for Web API controllers.

MVC Action filter across all the application

I am trying to create authorization action filter the will fire on each request to check if the user is allow to do some stuff.
So, i created the following classes/interfaces:
public interface IGlobalAuthorizationFilter : IGlobalFilter, IAuthorizationFilter
{
}
public interface IGlobalFilter
{
bool ShouldBeInvoked(ControllerContext controllerContext);
}
public class GlobalFilterActionInvoker : ControllerActionInvoker
{
protected FilterInfo GlobalFilters;
public GlobalFilterActionInvoker()
{
GlobalFilters = new FilterInfo();
}
public GlobalFilterActionInvoker(FilterInfo filters)
{
GlobalFilters = filters;
}
public GlobalFilterActionInvoker(IEnumerable<IGlobalFilter> filters)
: this(new FilterInfo())
{
foreach (IGlobalFilter filter in filters)
RegisterGlobalFilter(filter);
}
public FilterInfo Filters
{
get { return GlobalFilters; }
}
public void RegisterGlobalFilter(IGlobalFilter filter)
{
if (filter is IGlobalAuthorizationFilter)
GlobalFilters.AuthorizationFilters.Add((IGlobalAuthorizationFilter) filter);
}
protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
FilterInfo definedFilters = base.GetFilters(controllerContext, actionDescriptor);
foreach (IAuthorizationFilter filter in Filters.AuthorizationFilters)
{
var globalFilter = filter as IGlobalFilter;
if (globalFilter == null ||
(globalFilter.ShouldBeInvoked(controllerContext)))
{
definedFilters.AuthorizationFilters.Add(filter);
}
}
return definedFilters;
}
}
public class ApplicationControllerFactory : DefaultControllerFactory
{
private readonly IUnityContainer _container;
public ApplicationControllerFactory(IUnityContainer container)
{
this._container = container;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if ( controllerType == null )
{
throw new HttpException(404, "The file " + requestContext.HttpContext.Request.FilePath + " not found.");
}
IController icontroller = _container.Resolve(controllerType) as IController;
if (typeof(Controller).IsAssignableFrom(controllerType))
{
Controller controller = icontroller as Controller;
if (controller != null)
controller.ActionInvoker = _container.Resolve<IActionInvoker>();
return icontroller;
}
return icontroller;
}
}
And the class with the function that need to be called, but its not..
public class AuthenticationActionFilter : IGlobalAuthorizationFilter
{
public bool ShouldBeInvoked(System.Web.Mvc.ControllerContext controllerContext)
{
return true;
}
public void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
{
}
}
And, the Global.asax registration stuff:
IUnityContainer unityContainer = new UnityContainer();
unityContainer.RegisterType<IUserService, UserManager>();
unityContainer.RegisterType<IAppSettings, AppSettingsHelper>();
unityContainer.RegisterType<ICheckAccessHelper, CheckAccessHelper>().Configure<InjectedMembers>().ConfigureInjectionFor<CheckAccessHelper>(new InjectionConstructor());
unityContainer.RegisterType<IActionInvoker, GlobalFilterActionInvoker>().Configure<InjectedMembers>().ConfigureInjectionFor<GlobalFilterActionInvoker>(new InjectionConstructor());
unityContainer.RegisterType<IGlobalAuthorizationFilter, AuthenticationActionFilter>();
IControllerFactory unityControllerFactory = new ApplicationControllerFactory(unityContainer);
ControllerBuilder.Current.SetControllerFactory(unityControllerFactory);
So, as i said, my problem is the function: "ShouldBeInvoked" never called.
Any help?
I believe this filter would only be invoked on actions decorated with [Authorize] do you have that on the methods you want this filter to run?

Resources