Использование фильтра для выполнения другого действия? - PullRequest
2 голосов
/ 14 декабря 2009

Я хочу, чтобы в моих контроллерах не было много 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.

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

Приветствия

Ответы [ 2 ]

2 голосов
/ 14 декабря 2009

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

1 голос
/ 17 января 2010

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-вызова , который переходит к другому действию контроллера .

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

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

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

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

...