C# Action Filter для сериализации нескольких файлов resx в JSON

Я новичок в фильтрах действий в целом и всегда хотел знать, как они работают. Похоже, это хорошая возможность научиться...

Мы заменяем пользовательский интерфейс для приложения Castle Monorail, и я хотел бы сохранить функциональность или реализовать что-то похожее на атрибут [Resource], который он предоставляет в качестве настраиваемого фильтра действий, который записывает строки в область просмотра, чтобы мы могли писать их в объект JSON в глобальной области видимости и использовать их в JavaScript.

В настоящее время мы украшаем классы контроллера следующим образом (конечно, вместо этого я бы украшал методы контроллера):

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

Мой вопрос таков: «Как мне написать фильтр действий, который создает список ресурсов из отдельных использований и помещает их в переменную модели или представления?» ... или, возможно, ... "Как мне написать фильтр действий, который можно вызывать несколько раз, не перезаписывая данные предыдущих вызовов?"

Я читал учебные пособия и статьи, которые мог найти, но любая информация, которую я нашел о нескольких вызовах фильтра действий для одного метода контроллера, почти наверняка касается управления порядком выполнения, а не тем, как он фактически реализован.

Я приветствую любой код/псевдокод, чтобы демистифицировать это для меня.

Заранее спасибо


person Shane    schedule 17.06.2016    source источник


Ответы (1)


Во-первых, вы должны отказаться от идеи Microsoft «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; }
}

фильтр ресурсов

Тогда это просто вопрос использования Reflection для получения экземпляров атрибутов, точно так же, как вы могли бы получить экземпляры атрибутов где угодно. Существует удобный метод 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 только тогда, когда есть атрибуты для обработки.

person NightOwl888    schedule 18.06.2016