I have the following base controller with a string variable
public abstract class BaseController:Controller
{
string encryptedSessionGuid;
}
All other controller derives from base controller and ActionMethod has a custom ActionFilterAttribute CheckQueryString-
public class SampleController : BaseController
{
[CheckQueryString(new string[] {"sid"})]
public ActionResult SampleMethod()
{
return View();
}
}
Here is my custom attribute. It sends query string value to view. But I would like to send it base controller variable encryptedSessionGuid also.
public class CheckQueryString : ActionFilterAttribute
{
string[] keys;
public CheckQueryString(string[] Keys) { keys = Keys; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
foreach (var key in keys)
{
if (ctx.Request.QueryString[key] == null)
{
filterContext.Result = new RedirectResult(BulkSmsApplication.GlobalConfig.BaseUrl);
return;
}
else
{
string value = ctx.Request.QueryString[key];
if (string.IsNullOrEmpty(value))
{
filterContext.Result = new RedirectResult(BulkSmsApplication.GlobalConfig.BaseUrl);
return;
}
else
{
var viewBag = filterContext.Controller.ViewData;
viewBag[key] = value;
}
}
}
base.OnActionExecuting(filterContext);
}
}
How can it be done?
I have followed this tutorial in order to inject dependencies into a custom filter via an method attribute.
https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=98
Everything looks like its working, it hits the filter and calls the OnActionExecuting() method.
The problem I'm having is that when the user isn't authenticated, I want to return a custom response via context.Response, but it just carries on into the action result as-if the filter wasn't there.
Below are the various pieces of code taken from the above URL, plus my custom filter.
[Route("api/user/create")]
[AuthoriseRequest("Can_Create_User")]
[System.Web.Http.HttpPost]
.
public class AuthoriseRequest : Attribute
{
public string PermissionName { get; set; }
public AuthoriseRequest(string permissionName)
{
PermissionName = permissionName;
}
}
.
public class AuthoriseRequestActionFilter : IActionFilter<AuthoriseRequest>
{ ... }
.
public void OnActionExecuting(AuthoriseRequest request, HttpActionContext context)
{
var createUserRequest = (CreateUserRequest)context.ActionArguments["createUserRequest"];
var validator = _requestValidator.Validate(createUserRequest);
if (validator.IsValid())
{
var isAuthenticated =
_authenticationManager.AuthenticateRequest(...);
if (!isAuthenticated)
{
//THIS DOESN'T SET THE RESPONSE
var unauthorisedRequestResponse = new APIResponse<string>(null, false, validator.ToString());
context.Response = context.Request.CreateResponse(
HttpStatusCode.OK,
unauthorisedRequestResponse);
}
}
}
.
public sealed class ActionFilterDispatcher : IActionFilter
{
private readonly Func<Type, IEnumerable> container;
public ActionFilterDispatcher(Func<Type, IEnumerable> container)
{
this.container = container;
}
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext context,
CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
var descriptor = context.ActionDescriptor;
var attributes = descriptor.ControllerDescriptor.GetCustomAttributes<Attribute>(true)
.Concat(descriptor.GetCustomAttributes<Attribute>(true));
foreach (var attribute in attributes)
{
Type filterType = typeof(IActionFilter<>).MakeGenericType(attribute.GetType());
IEnumerable filters = this.container.Invoke(filterType);
foreach (dynamic actionFilter in filters)
{
actionFilter.OnActionExecuting((dynamic)attribute, context);
}
}
return continuation();
}
public bool AllowMultiple { get { return true; } }
}
.
public static void RegisterGlobalFilters(GlobalFilterCollection filters, Container container)
{
GlobalConfiguration.Configuration.Filters.Add(new ActionFilterDispatcher(container.GetAllInstances));
container.RegisterCollection(typeof(IActionFilter<>), typeof(IActionFilter<>).Assembly);
filters.Add(new HandleErrorAttribute());
}
Thanks.
I have this code snippet to using MvxExpandableListView
public class ExpandView : MvxActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.ExpandView);
MvxExpandableListView list = FindViewById<MvxExpandableListView>(Resource.Id.TheExpandView);
list.SetAdapter(new MyCustomAdaptor(this, (IMvxAndroidBindingContext)BindingContext));
}
private class MyCustomAdaptor : MvxExpandableListAdapter
{
private IList _itemsSource;
public MyCustomAdaptor(Context context, IMvxAndroidBindingContext bindingContext)
: base(context, bindingContext)
{
}
protected override void SetItemsSource(IEnumerable value)
{
Mvx.Trace("Setting itemssource");
if (_itemsSource == value)
return;
var existingObservable = _itemsSource as INotifyCollectionChanged;
if (existingObservable != null)
existingObservable.CollectionChanged -= OnItemsSourceCollectionChanged;
_itemsSource = value as IList;
var newObservable = _itemsSource as INotifyCollectionChanged;
if (newObservable != null)
newObservable.CollectionChanged += OnItemsSourceCollectionChanged;
if (value != null)
{
}
else
base.SetItemsSource(null);
}
}
}
When the view is loaded the override method SetItemsSource does not get called , but when I try to navigate away from the page SetItemsSource gets called with a null value.
You need to set the property ItemsSource.Looking at the source of MvxAdapter:
[MvxSetToNullAfterBinding]
public virtual IEnumerable ItemsSource
{
get { return _itemsSource; }
set { SetItemsSource(value); }
}
The setter of ItemsSource calls the method SetItemsSource.
MvxExpandableListAdapter inherits MvxAdapter.
Source:
https://github.com/MvvmCross/MvvmCross/blob/4.0/MvvmCross/Binding/Droid/Views/MvxExpandableListAdapter.cs
https://github.com/MvvmCross/MvvmCross/blob/4.0/MvvmCross/Binding/Droid/Views/MvxAdapter.cs
I have set up a global filter for all my controller actions in which I open and close NHibernate sessions. 95% of these action need some database access, but 5% don't. Is there any easy way to disable this global filter for those 5%. I could go the other way round and decorate only the actions that need the database, but that would be far more work.
You could write a marker attribute:
public class SkipMyGlobalActionFilterAttribute : Attribute
{
}
and then in your global action filter test for the presence of this marker on the action:
public class MyGlobalActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.ActionDescriptor.GetCustomAttributes(typeof(SkipMyGlobalActionFilterAttribute), false).Any())
{
return;
}
// here do whatever you were intending to do
}
}
and then if you want to exclude some action from the global filter simply decorate it with the marker attribute:
[SkipMyGlobalActionFilter]
public ActionResult Index()
{
return View();
}
Though, the accepted answer by Darin Dimitrov is fine and working well but, for me, the simplest and most efficient answer found here.
You just need to add a boolean property to your attribute and check against it, just before your logic begins:
public class DataAccessAttribute: ActionFilterAttribute
{
public bool Disable { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Disable) return;
// Your original logic for your 95% actions goes here.
}
}
Then at your 5% actions just use it like this:
[DataAccessAttribute(Disable=true)]
public ActionResult Index()
{
return View();
}
In AspNetCore, the accepted answer by #darin-dimitrov can be adapted to work as follows:
First, implement IFilterMetadata on the marker attribute:
public class SkipMyGlobalActionFilterAttribute : Attribute, IFilterMetadata
{
}
Then search the Filters property for this attribute on the ActionExecutingContext:
public class MyGlobalActionFilter : IActionFilter
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.Filters.OfType<SkipMyGlobalActionFilterAttribute>().Any())
{
return;
}
// etc
}
}
At least nowadays, this is quite easy: to exclude all action filters from an action, just add the OverrideActionFiltersAttribute.
There are similar attributes for other filters: OverrideAuthenticationAttribute, OverrideAuthorizationAttribute and OverrideExceptionAttribute.
See also https://www.strathweb.com/2013/06/overriding-filters-in-asp-net-web-api-vnext/
Create a custom Filter Provider. Write a class which will implement IFilterProvider. This IFilterProvider interface has a method GetFilters which returns Filters which needs to be executed.
public class MyFilterProvider : IFilterProvider
{
private readonly List<Func<ControllerContext, object>> filterconditions = new List<Func<ControllerContext, object>>();
public void Add(Func<ControllerContext, object> mycondition)
{
filterconditions.Add(mycondition);
}
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
return from filtercondition in filterconditions
select filtercondition(controllerContext) into ctrlContext
where ctrlContext!= null
select new Filter(ctrlContext, FilterScope.Global);
}
}
=============================================================================
In Global.asax.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
MyFilterProvider provider = new MyFilterProvider();
provider.Add(d => d.RouteData.Values["action"].ToString() != "SkipFilterAction1 " ? new NHibernateActionFilter() : null);
FilterProviders.Providers.Add(provider);
}
protected void Application_Start()
{
RegisterGlobalFilters(GlobalFilters.Filters);
}
Well, I think I got it working for ASP.NET Core.
Here's the code:
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// Prepare the audit
_parameters = context.ActionArguments;
await next();
if (IsExcluded(context))
{
return;
}
var routeData = context.RouteData;
var controllerName = (string)routeData.Values["controller"];
var actionName = (string)routeData.Values["action"];
// Log action data
var auditEntry = new AuditEntry
{
ActionName = actionName,
EntityType = controllerName,
EntityID = GetEntityId(),
PerformedAt = DateTime.Now,
PersonID = context.HttpContext.Session.GetCurrentUser()?.PersonId.ToString()
};
_auditHandler.DbContext.Audits.Add(auditEntry);
await _auditHandler.DbContext.SaveChangesAsync();
}
private bool IsExcluded(ActionContext context)
{
var controllerActionDescriptor = (Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)context.ActionDescriptor;
return controllerActionDescriptor.ControllerTypeInfo.IsDefined(typeof(ExcludeFromAuditing), false) ||
controllerActionDescriptor.MethodInfo.IsDefined(typeof(ExcludeFromAuditing), false);
}
The relevant code is in the 'IsExcluded' method.
You can change your filter code like this:
public class NHibernateActionFilter : ActionFilterAttribute
{
public IEnumerable<string> ActionsToSkip { get; set; }
public NHibernateActionFilter(params string[] actionsToSkip)
{
ActionsToSkip = actionsToSkip;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (null != ActionsToSkip && ActionsToSkip.Any(a =>
String.Compare(a, filterContext.ActionDescriptor.ActionName, true) == 0))
{
return;
}
//here you code
}
}
And use it:
[NHibernateActionFilter(new[] { "SkipFilterAction1 ", "Action2"})]
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?