Вернуть частичное представление и JSON из ASP.NET MVC Action

Я представляю KnockoutJS в существующем приложении. Мой план состоит в том, чтобы изменить / использовать существующие частичные представления, которые мы уже создали, и связать их с моделями представления JS с декларативными атрибутами Knockout. Когда я выполняю AJAX-вызов для действия, в идеале я хотел бы, чтобы это действие возвращало как HTML-код частичного представления, так и объект JSON. Затем я могу заполнить div с помощью HTML, преобразовать JSON в объект Knockout и связать его с HTML. Но я не могу понять, как вернуть оба из действия.

Мне нужна модель полного представления, потому что я буду обновлять ее и в конечном итоге отправлять обратно на сервер.

Я думал о том, чтобы действие вернуло частичное представление (уже привязанное к модели), и в частичное представление включил JavaScript, чтобы преобразовать модель.Net в объект Knockout. Но я чувствую, что разбрасывать JS вокруг, это грязно и неустранимо. Я бы предпочел, чтобы все было близко к исходному вызову Ajax.

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

Есть идеи, как лучше всего это сделать?

2 ответа

Решение

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

Это сохраняет ответственность каждого субъекта. Представления по-прежнему расположены с использованием механизма просмотра, и их можно использовать повторно. Контроллер мало или ничего не знает о представлении, кроме его имени и типа модели.

Ручной рендеринг

public static class RenderHelper
{
    public static string PartialView( Controller controller, string viewName, object model )
    {
        controller.ViewData.Model = model;

        using( var sw = new StringWriter() )
        {
            var viewResult = ViewEngines.Engines.FindPartialView( controller.ControllerContext, viewName );
            var viewContext = new ViewContext( controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw );

            viewResult.View.Render( viewContext, sw );
            viewResult.ViewEngine.ReleaseView( controller.ControllerContext, viewResult.View );

            return sw.ToString();
        }
    }
}

В вашем методе действий:

object model = null; // whatever you want
var obj = new { 
    someOtherProperty = "hello", 
    view = RenderHelper.PartialView( this, "_PartialName", model ) 
};

return Json( obj );

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

тестирование

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

Ручной рендеринг

  1. Введите метод действия
  2. Представление представления явно <- это затруднит проверку вызывающего действия
  3. Метод действия выхода

Автоматический рендеринг

  1. Введите метод действия
  2. Создать результат просмотра
  3. Метод действия выхода
  4. Результат представления процесса (таким образом, представление представления)

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

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

    public static string PartialView( Controller controller, string viewName, object model )
    {
        // returns false from a VS 2013 unit test, true from IIS
        if( !HostingEnvironment.IsHosted )
        {
            // return whatever you want here
            return string.Empty;
        }

        // continue as usual
     }

проверка HostingEnvironment.IsHosted стоит недорого (под капотом это просто нулевая проверка).

Вы можете создать скрытый <input> на партиале со значением, установленным в строку JSON ViewModel. Затем перед рендерингом частичного представления извлеките значение JSON из этого поля и проанализируйте его. Затем удалите его из частичного представления, вставьте на свою страницу и выполните ko.applyBindingsToDescendants(viewModel, $("#parentElement")[0])

Я не совсем уверен, что я чувствую по поводу этого подхода, и это просто теория. Я не проверял это, но подозреваю, что это сработает. Одна из ловушек, на которую вы должны обратить внимание - это браузер, пытающийся кэшировать ваш запрос GET. В вашем ajax-запросе вы хотели бы сделать:

$.ajax({
  url: "/",
  type: 'GET',
  cache: 'false'
});

Или просто сделай $.post запрос. ( ссылка)

Так что это один из вариантов.

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