MVC Компонент с обратной передачей

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

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

Вот упрощенный пример (не то, чтобы код не мог быть скомпилирован, так как я сократил свой синтаксический сахар, чтобы упростить его до простого MVC):

Просмотреть код (использование компонента):

@Html.Action(
    "Default",
    "FacebookFeed",

    new {
        // I don't want this data to pass through client
        settings = new FacebookFeedSettings {
            AppKey = "XYZ",
            AppSecret = "123",
            PageSize = 10
        }
        .ItemTemplate(
            @<div class="feed-item">@item.Title</div>
        )
    }
)

Код контроллера:

public class FacebookFeedController {
   public ActionResult Default(FacebookFeedSettings settings)
   {
      // Action code using settings

      return PartialView(model);
   }
}

Код просмотра фида:

@using (Ajax.BeginForm("Default", "FacebookFeed", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.ReplaceWith }))
{
    // Form code

    <input type="submit" value="Refresh" />
}

Поэтому, когда нажата кнопка "Обновить", она должна отображать свежие данные, но в этом запросе отсутствуют параметры.

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

Модифицированный код вида (использование компонента):

@Html.Action(
    "Default",
    "FacebookFeed",
    new {
        settingsKey = "HomePageFBFeed"
    }
)

Дополнительный код от пользователя:

[ComponentSettings]
public class HomePageFBFeed : FacebookFeedSettings
{
    public HomePageFBFeed()
    {
        AppKey = "XYZ";
        AppSecret = "123";
        PageSize = 10;
    }
}

Модифицированный код контроллера:

public ActionResult Default(string settingsKey)
{
   FacebookFeedSettings settings = ComponentSettings.GetSettings(settingsKey);

   // Action code using settings

   return PartialView(model);
}

Модифицированный код вида:

@using (Ajax.BeginForm("Default", "FacebookFeed", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.ReplaceWith }, new { settingsKey = Model.SettingsKey }))
{
   ...
}

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

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

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

Есть ли какой-нибудь лучший способ / лучшие практики, как это сделать в ASP.NET 4.6 / MVC 5?

Если нет, будет ли это возможно в ASP.NET 5 / MVC 6?

2 ответа

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

Хорошей новостью является то, что MVC 6, кажется, имеет ответ на вашу проблему. Прежде всего, дочерние действия больше не существуют, их заменяют View Components. Компоненты представления состоят из класса C# и представления Razor и не зависят от контроллера, что облегчает повторное использование.

У меня пока нет прямого опыта, но из того, что я читаю о MVC 6 и, в частности, View Components, они автономны, поэтому в основном вы можете управлять состоянием внутри самих компонентов. Параметры не передаются по HTTP из представления, потому что вы фактически вызываете компонент на стороне сервера (без возврата клиента).

Кроме того, компоненты View не участвуют в жизненном цикле контроллера, но у вас все еще есть доступ к ViewBag и ViewData (совместно используемым с контроллером). Наконец, как и контроллеры, View Components также участвуют в внедрении зависимостей, поэтому любая другая необходимая информация может быть просто внедрена в компонент View.

Это должно работать для вас, но вам нужно подождать MVC 6:-)

Мартин, я знаю, что вы сказали, что не хотите создавать состояние просмотра, но, учитывая отключенный режим MVC (вы не хотите использовать серверную сессию, в том числе и потому, что она не масштабируется), вы были бы открыты передать зашифрованную строку настроек?

Как то так во взгляде:

@using (Ajax.BeginForm("Default", "FacebookFeed", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.ReplaceWith }))
{
    @Html.AntiForgeryToken()
    <input type="hidden" name="Settings" value="@Model.ToEncryptedString()" />
    <input type="submit" value="Refresh" />
}

ToEncryptedString() будет расширением вашей модели, которое:

  1. Сериализация ваших настроек (объект FacebookFeedSettings)
  2. Зашифруй это
  3. Конвертировать в Base 64, чтобы сделать его дружественным к HTTP

При возвращении к контроллеру все, что вам нужно сделать, это прочитать параметр Settings в действии Default:

[HttpPost, ValidateAntiForgeryToken]
public ActionResult Default(string settings)
{
    FacebookFeedSettings facebookSettings = FacebookFeedSettings.FromEncryptedString(settings);
    // do something with the settings
    // build the model
    return PartialView(model);
}

FromEncryptedString() делает в противоположном направлении:

  1. Конвертировать из Base 64 в байт []
  2. Расшифровать массив байтов
  3. Десериализовать в экземпляр объекта FacebookFeedSettings

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

[HttpPost, ValidateAntiForgeryToken, FacebookFeedSettings]
public ActionResult Default(/* No need to capture settings here */)

Атрибут FacebookFeedSettingsAttribute получит параметр Settings из запроса и создаст и проверит экземпляр объекта FacebookFeedSettings. Я не пытался зайти так далеко в своей попытке, я оставляю это на практике:-)

Как вы думаете?

Другие вопросы по тегам