Вопрос

Я хочу избежать большого количества if Request.IsAjaxRequest() в моих контроллерах.Я думал, что если бы я мог сжать эту логику до ActionFilter, было бы легко принять в моем приложении соглашение, обеспечивающее второе действие для любого запроса, который может используйте Ajax, обеспечивая при этом запасной вариант, если JavaScript отключен.

public ActionResult Details(int id)
{
  // called normally, show full page
}

public ActionResult Details_Ajax(int id)
{
  // called through ajax, return a partial view
}

Сначала я думал, что смогу сделать что-то вроде этого:

public class AjaxRenameAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.RouteData.Values["action"] = filterContext.RouteData.Values["action"] + "_Ajax";

    }

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

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

Есть ли другой способ перенаправить запрос к другому действию?Или то, что я делаю, нецелесообразно, и мне следует поискать лучший способ сделать что-то?

Ваше здоровье

Это было полезно?

Решение

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

Не все запросы Ajax одинаковы.Запрос Ajax может пытаться выполнить любую из следующих задач:

  • Привязка данных JSON к расширенной сетке (например, jqGrid);
  • Анализ/преобразование XML-данных, таких как RSS-канал;
  • Загрузка частичного HTML в область страницы;
  • Асинхронная загрузка скрипта (google.load могу это сделать);
  • Обработка одностороннего сообщения от клиента;
  • И, возможно, еще несколько, о которых я забыл.

Когда вы «выбираете» конкретное «альтернативное действие», основываясь исключительно на IsAjaxRequest метод, вы привязываете что-то очень общее — асинхронный запрос — к конкретным функциям сервера.В конечном итоге это сделает ваш проект более хрупким, а также затруднит модульное тестирование вашего контроллера (хотя есть способы сделать это, вы можете имитировать контекст).

Хорошо продуманный действие должно быть последовательный, его должно волновать только что запрос был за, а не за как запрос был сделан.Можно указать на другие атрибуты, такие как AuthorizeAttribute В качестве исключений я бы выделил фильтры, которые в большинстве случаев описывают поведение, которое должно произойти либо «до», либо «после» совершения действия, а не «вместо».

Переходя к сути, цель, заявленная в вопросе, является хорошей;вам следует определенно имеют разные методы для того, что правильно описывается как разные действия:

public ActionResult Details(int id)
{
    return View("Details", GetDetails(id));
}

public ActionResult JsonDetails(int id)
{
    return Json(GetDetails(id));
}

public ActionResult PartialDetails(int id)
{
    return PartialView("DetailTable", GetDetails(id));
}

И так далее.Однако использование селектора действий Ajax для выбора между этими методами соответствует практике «изящная деградация», который по существу был заменен (по крайней мере, IMO) на прогрессивное улучшение.

Вот почему, хотя я и люблю ASP.NET MVC, я в основном избегаю AjaxHelper, потому что я не считаю, что это хорошо выражает эту концепцию;он пытается скрыть от вас слишком многое.Вместо того, чтобы использовать концепцию «формы Ajax» или «действия Ajax», давайте покончим с различием и будем придерживаться простого HTML, а затем внедрить функциональность Ajax отдельно, как только мы будем уверены, что клиент справится с ней.

Вот пример в jQuery, хотя вы можете сделать это и в MS AJAX:

$(function() {
    $("#showdetails").click(function() {
        $("#details").load("PartialDetails", { id: <%= Record.ID %> });
        return false;
    }
});

Это все, что нужно для внедрения Ajax на страницу MVC.Начните с простой старой ссылки HTML и переопределите ее с помощью вызова Ajax. это переходит в другое действие контроллера.

Теперь, если где-то на вашем сайте вы решите использовать вместо этого сетку, но не хотите разбивать страницы с помощью частичного рендеринга, вы можете написать что-то вроде этого (скажем, у вас есть одна страница master-detail с список «заказов» слева и подробная таблица справа):

$(".detaillink").click(function() {
    $('#detailGrid').setGridParam({
        url: $(this).attr("href").replace(/\/order\/details/i,
            "/order/jsondetails")
    }); 
    $("#detailGrid").trigger("reloadGrid");  
});

Этот подход полностью отделяет поведение клиента от поведения сервера.Сервер фактически говорит клиенту: Если вам нужна версия JSON, просить для версии JSON, и, кстати, вот скрипт для преобразования ваших ссылок, если вы знаете, как его запустить. Никаких селекторов действий и возни с перегрузками методов, никаких особых издевательств, которые нужно делать, чтобы запустить простой тест, никакой путаницы в отношении того, какое действие, что и когда делает.Всего пара строк JavaScript.Действия контроллера короткие и приятные, именно такими, какими и должны быть.

Это не единственный подход.Очевидно, что такие классы, как AcceptAjaxAttribute существуют, потому что они ожидали, что некоторые разработчики будут использовать метод обнаружения запросов.Но, поэкспериментировав с обоими, я нашел такой способ. много об этом легче рассуждать и, следовательно, легче правильно проектировать/кодировать.

Другие советы

Как насчет AcceptAjaxAttribute в MvcFutures?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top