Можно ли сделать данные из метода действия доступными в фильтре действий? - PullRequest
2 голосов
/ 09 сентября 2010

Фон:

Мы поставляем html-файлы - «обертки» - от нашего клиента, в которые нам нужно внедрить производимый нами контент. У них разные оболочки для разных страниц, и мы должны вставить соответствующий контент в специальный тег, который они предоставляют в оболочке.

Имя файла оболочки соответствует имени метода действия. Таким образом, для приведенного ниже примера выполняется фильтр настраиваемых действий, который определит имя оболочки и затем вызовет метод в BaseController (который реализует каждый контроллер), который загрузит оболочку и вставит в нее наш контент.

    [WrapperAction]
    public ActionResult Home()
    {
        return View();
    }

Причина, по которой я поместил это в ActionFilter, заключается в том, что я не хотел вызывать метод BaseController, чтобы заполнить оболочку в каждом методе действия, который нуждался в оболочке. Я подумал, что гораздо уместнее украсить метод с помощью ActionFilterAttribute

WrapperAction определяется следующим образом:

public class WrapperAction : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext aec)
    {
        var baseController = (BaseController)filterContext.Controller;

        string wrapper = aec.RequestContext.RouteData.Values["action"].ToString();

        baseController.PopulateWrapper(wrapper);
    }
}

и PopulateWrapper определяется как

    public void PopulateWrapper(string wrapperName)
    {
        // get wrapper file from disk
        Wrapper wrapper = _wrapperService.GetWrapper(Site.Id, wrapperName);

        // populate the file with our CSS (it already has its own pre-populated CSS)
        // our CSS file is determined by the id of a Site object.
        AppHelper.PopulateWrapperCss(wrapper, this.Site.Id);

        // make wrapper sections available to the Master page, 
        // split so that we can place our content around them
        ViewData["WrapperTop"] = wrapper.WrapperTop;
        ViewData["WrapperMiddle"] = wrapper.WrapperMiddle;
        ViewData["WrapperBottom"] = wrapper.WrapperBottom;
    }

Дилемма:

Однако теперь появилось новое требование. В оболочке есть несколько новых тегов, которые мне нужно заполнить, и мне нужно заполнить их различными данными в зависимости от метода действия, который вызывает оболочку. Эти данные определяются в методах действия, но эти данные необходимо использовать в методе PopulateWrapper, который вызывается WrapperAction.

Теперь мне нужен метод, аналогичный

AppHelper.PopulateWrapperTags(wrapper, this.TagData);

и мне нужно, чтобы свойство TagData BaseController заполнялось данными. Я могу присвоить свойство в методе действия следующим образом

    [WrapperAction]
    public ActionResult Home()
    {
        base.TagData = GetTagData();
        return View();
    }

но такой вид поражает тот факт, что в первую очередь я имею в виду действие WrapperAction, потому что я не хочу, чтобы я так обращался к BaseController.

Вопрос:

Есть ли способ предоставить WrapperAction данные, необходимые для заполнения оболочки? Нужно ли принять удар и начать звонить

        var tagData = GetTagData();
        string wrapperName = RouteData.Values["action"].ToString();
        base.PopulateWrapper(wrapperName, tagData);

в каждом контроллере? Есть ли лучший способ для меня это сделать?

Ответы [ 2 ]

1 голос
/ 09 сентября 2010

То, что вы уже написали, действительно очень хорошо и требует лишь небольшой корректировки, чтобы соответствовать вашему новому требованию.

Ключ в том, что ваш фильтр действий относится к типу OnActionExecuted, который запускается после ваш код действия завершен, но до представление выполняется.Поэтому метод фильтра может основываться на том, что имело место в методе действия.

Чтобы выполнить ваше требование, создайте переменную tagData в вашем базовом контроллере, чтобы контроллеры, наследующие его, могли заполнить ее, еслинеобходимо.

Тогда в вашем фильтре действий вам просто нужно что-то вроде

if (tagData == null)
   baseController.PopulateWrapper(wrapper)
else
   baseController.PopulateWrapper(wrapper, tagData)

Я уверен, что вы поняли основную идею.

0 голосов
/ 09 сентября 2010

imho следующее было просто правильно, так что я не вижу, как фильтр действий более аккуратный:

public ActionResult Home()
{
   PopulateWrapper();
   return View();
}

и в базе контроллера:

public void PopulateWrapper(string wrapperName)
{
   string wrapperName = RouteData.Values["action"].ToString();
   //... rest of populate wrapper

тогда вы идете:

public ActionResult Home()
{
    PopulateWrapper(GetTagData()); // after defining an overload in base
    return View();
}

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

Обратите внимание, что вы также можете передавать данные в ViewData, но это выглядит хуже всего в вашем сценарии - особенно с учетом зависимости в базе контроллера.

...