Вернуть частичное представление и 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.
Ручной рендеринг
- Введите метод действия
- Представление представления явно <- это затруднит проверку вызывающего действия
- Метод действия выхода
Автоматический рендеринг
- Введите метод действия
- Создать результат просмотра
- Метод действия выхода
- Результат представления процесса (таким образом, представление представления)
Другими словами, наш ручной процесс рендеринга запускает множество других операций, которые затрудняют тестирование (например, взаимодействие с менеджером сборки для компиляции представления).
Предполагая, что вы хотите проверить метод действия, а не фактическое содержимое представления, вы можете проверить, выполняется ли код в размещенной среде.
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
запрос.
( ссылка)
Так что это один из вариантов.