1

总的来说,我是 Action Filters 的新手,一直想知道它是如何工作的。这似乎是一个学习的好机会......

我们正在替换 Castle Monorail 应用程序的 UI,我想保留或实现类似于它作为自定义操作过滤器提供的 [Resource] 属性的功能,该过滤器将字符串写入视图包,以便我们可以编写将它们转换为全局范围内的 JSON 对象,并在 JavaScript 中使用它们。

目前,我们像这样装饰控制器类(当然我会装饰控制器方法):

[Resource("common", "namespace.Resources.Common")]
[Resource("events", "namespace.Resources.Events")]
[Resource("people", "namespace.Resources.People")]
public class someController : BaseController

我的问题是:“我如何编写一个动作过滤器,从个人使用中构建资源列表并将它们存放到模型或视图包变量中?” ......或者可能......“我如何编写一个可以多次调用的操作过滤器而不覆盖以前调用的数据?”

我已经阅读了我能找到的教程和文章,但是我发现的任何关于在单个控制器方法上多次调用动作过滤器的信息几乎肯定是关于控制执行顺序而不是它的实际实现方式。

我欢迎任何代码/伪代码为我揭开这个神秘面纱。

提前致谢

4

1 回答 1

1

首先,你必须与微软的“ActionFilterAttribute”理念脱节。这将所有逻辑放在一个地方,但它令人困惑,因为属性和过滤器是两个完全不同的东西。

在这种情况下,您需要将它们分开,以便过滤器可以检测多个属性并对数据执行您想要的操作(这有点不清楚,但我想您会将某种列表放入 ViewData)。

资源属性

首先,我们有属性定义。它需要用 AttributeUsage 属性特别标记,以便可以多次使用,因此可以在控制器和操作方法上使用(我假设您希望聚合当前控制器和操作上的所有属性,但是你不必那样做)。

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public class ResourceAttribute : Attribute
{
    public ResourceAttribute(string name, string value)
    {
        this.Name = name;
        this.Value = value;
    }

    public string Name { get; private set; }
    public string Value { get; private set; }
}

资源过滤器

然后只需使用反射来获取属性实例,就像在任何地方获取属性实例一样。有一种方便的ActionDescriptor方法可以让您直接访问动作和控制器类型。

上下文还使您可以直接访问ViewData.

public class ResourceFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var attributes = this.GetAllAttributes(filterContext.ActionDescriptor);

        foreach (ResourceAttribute attribute in attributes)
        {
            var name = attribute.Name;
            var value = attribute.Value;

            // Do something with the meta-data from the attribute...
        }

        if (attributes.Any())
        {
            // Set the ViewData only when there are attributes
            filterContext.Controller.ViewData["SomeKey"] = "SomeValue";
        }
    }

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        // Do nothing
    }

    public IEnumerable<ResourceAttribute> GetAllAttributes(ActionDescriptor actionDescriptor)
    {
        var result = new List<ResourceAttribute>();

        // Check if the attribute exists on the action method
        result.AddRange(
            actionDescriptor
                .GetCustomAttributes(typeof(ResourceAttribute), false) as ResourceAttribute[]
        );

        // Check if the attribute exists on the controller
        result.AddRange(
            actionDescriptor
                .ControllerDescriptor
                .GetCustomAttributes(typeof(ResourceAttribute), false) as ResourceAttribute[]
        );

        return result;
    }
}

用法

现在,您只需将这些部件放置到位。首先,注册动作过滤器。

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        // Register the filter globally
        filters.Add(new ResourceFilter());
        filters.Add(new HandleErrorAttribute());
    }
}

然后相应地装饰您的控制器和操作。过滤器每次都会运行,因此您需要确保其中有一个条件,使其仅在有要处理的属性时才输出 ViewData。

于 2016-06-18T01:44:36.270 回答