Частичные взгляды против Json (или оба)
Я использую ASP.NET MVC с jQuery и имею много запросов Ajax для моих контроллеров.
Использовать частичные представления (usercontrols) для создания внутреннего представления при загрузке страницы. Затем, если мне нужно добавить/заменить данные на основе моего запроса Ajax, я создаю HTML из ответа Json.
Этот подход дает мне полный контроль, т.е. Я могу получить дополнительную информацию с моего контроллера, если что-то пошло не так, а затем отобразите на нем сообщение об ошибке.
Тем не менее, в последнее время меня очень раздражало все дополнительные работы, связанные с поддержкой структуры HTML как в моих частичных представлениях, так и в части, которая генерирует HTML из Json.
Мне нравится делать запрос ajax jQuery, а затем возвращать контроллер PartialView ( "mypartialview" ), а затем просто использовать jQuery для замены HTML в представлении.
Однако таким образом я не могу приложить дополнительные данные от контроллера - это либо то, что дает мне частичный вид, либо ничего. По крайней мере, мой нынешний подход к этому.
Если в какой-то момент моего действия контроллера некорректная проверка не выполняется, я не хочу возвращать HTML частичного представления.
Итак, как вы решаете эту проблему?
Спасибо за чтение.
Ответы
Ответ 1
На основе fooobar.com/questions/16451/...
Сначала создайте метод расширения для класса контроллера.
public static string RenderViewToString(this Controller controller, string viewName, object model)
{
using (var writer = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
controller.ViewData.Model = model;
var viewCxt = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, writer);
viewCxt.View.Render(viewCxt, writer);
return writer.ToString();
}
}
Затем верните json в метод действий с контроллерами.
return Json(new
{
Html = this.RenderViewToString("MyView", model),
SomeExtraData = data
});
Ваши запросы ajax теперь получат json с содержащимся в нем html. Все еще экспериментирует с этим подходом к возврату простых фрагментов Html.
Надеюсь, что это поможет.
EDIT Обновлено для работы с бритвой
Ответ 2
Ниже приведен пример использования Content-Type
, возвращаемого каждым соответствующим результатом. Мы используем это для отправки информации об ошибке, и там уже есть JS для отображения сообщений, поэтому мы получаем либо частичное представление, которое мы хотим, либо управляем информацией для сообщений об ошибках и т.д.
Для справки, частичный вид возвращает text/html
, и ответ JSON должен возвращать application/json
.
Как обычно, забавная часть находится на стороне javascript, и JQuery ajax()
здесь не разочаровывает!
В вашем контроллере просто верните либо PartialView()
, либо Json(model,)
, если это необходимо; мы используем его в формате try/catch
.
public ActionResult Edit(int id) {
try {
var model = getYourModel();
return PartialView("Edit", model);
}
catch (Exception ex) {
var mi = new MessageInfo(MessageType.Error, String.Format("Edit failed: {0}", ex.Message), true);
return Json(mi, "application/json", JsonRequestBehavior.AllowGet);
}
}
На стороне JS мы используем следующую функцию. Обратите внимание, что вам необходимо заново установить любые события JQuery, которые вы подключили в $(document).ready()
, от начального уровня GET на уровне страницы, поэтому у нас есть параметр обратного вызова.
function getPartialView(action, controller, model, divId, callback) {
var url = "/" + controller + "/" + action + "/";
$.ajax({
type: "GET",
url: url,
data: model,
success: function (data, textStatus, jqXHR) {
var ct = jqXHR.getResponseHeader("Content-Type");
var mx = ct.match("text\/html");
if (mx != null) {
$(divId).html(data);
if (callback) {
callback($(divId));
}
}
else {
addMessage(data.type, data.title, data.text, data.sticky);
}
},
error: function (jqXHR, textStatus, errorThrown) {
addMessage(3, "\"" + url + "\": Failed", textStatus + ": " + errorThrown, false);
}
});
}
Единственный сложный бит - проверка заголовка Content-Type
в ответе и поведение соответственно. Обратите внимание, что мы "обманываем", предполагая JSON, если это не HTML. Мы называем нашу ранее существовавшую функцию addMessage()
, делаем все, что вам нужно!
И, наконец, вот пример элемента Anchor с onclick
таргетингом getPartialView()
выше.
<a href="#" onclick="getPartialView('Action', 'Controller', model, '#partialviewdivid', function(dvx) { connectJqueryEvents(dvx); })">Cancel</a>
Работает отлично...
За исключением того, что форма отправляется через Ajax.BeginForm()
, где полезная нагрузка JSON ошибочно обрабатывается как HTML из-за недостаточной проверки Content-Type
. В результате ваш div
получает некоторый JSON, добавленный к нему, который в основном не отображает как HTML. Обратный вызов AjaxOptions.OnSuccess
выполняется, но это слишком поздно для вашей DOM в этот момент!
Существует простое решение, но, к сожалению, он требует небольшого ремонта до jquery-unobtrusive-ajax.js
, потому что функция asyncOnSuccess()
была близорука, как написано.
function asyncOnSuccess(element, data, contentType) {
var mode;
if (contentType.indexOf("application/x-javascript") !== -1) {
return;
}
if (contentType.indexOf("application/json") !== -1) {
return;
}
...snip...
}
В версии OOTB отсутствует второй оператор if
; добавив, что это исправление, необходимое для того, чтобы не допустить его взлома вашей полезной нагрузки JSON в DOM.
С помощью этого исправления полезная нагрузка JSON переходит в ваш AjaxOptions.OnSuccess
Javascript, и вы можете действовать по мере необходимости.
Да, вы можете получить как
Надеюсь, вы знаете, что когда вы отправляете Json, вы можете отправить любую модель и позволить Javascript разобраться в этом; hasOwnProperty()
пригодится там. Поэтому, очевидно, вы можете отправить какой-либо HTML-код с помощью уже упомянутого RenderViewToString()
.
Ответ 3
Я считаю, что вы могли бы вернуть отображаемый html в виде строки - это может быть альтернативная строка html, содержащая сообщение об ошибке для отображения?
Ответ 4
Вы также можете сделать это, Поместите это внутри своего контроллера.
protected string RenderPartialViewToString(string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
viewName = ControllerContext.RouteData.GetRequiredString("action");
ViewData.Model = model;
using (StringWriter sw = new StringWriter()) {
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}