Comments 19
this.Request.IsAjaxRequest() в любом методе контроллера, заменяет все ваши магические действия по определению заголовка X-Requested-With
А также избавит от глупостей с «префиксами» методов:

Будет примерно вот так:
[HttpGet]
public ViewResult About()
{
if (this.Request.IsAjaxRequest()) return PartialView("About");
return View("About");
}
А можно еще и вынести проверку на IsAjaxRequest в базовый класс и будет вовсе красота:
public ViewResult About()
{
return PartialOrFullView("About");
}

Ну и еще из плюсов «одного метода» — сылка в браузере будет вести на человеческий "/Home/About", а не роботизированный "/Home/AboutAjax".

собственно, подход более-менее очевидный, в своем проекте реализовал очень схожим образом. Разве что Html.PageInfo на Razor section было.

Но статья всё равно неплохая :)
Как по мне именнованое соглашение лучше условных операторов внутри метода и божественного метода PartialOrFullView, а разрешение необходимого метода контроллера по унифицированной ссылке отдать тому, кто в ответе за это по праву — своей реализации Routing обработчика
public ViewResult About(int param1) {
var model = CreateAboutModel(param1);
return View(model);
}
public ViewResult AboutAjax(int param1) {
var model = CreateAboutModel(param1);
return View("About", model);
}
public AboutViewModel CreateAboutModel(int param1) {
return new AboutViewModel() {/*some calculations go here */}
}

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

Если в большинстве случаев логика аякс и не-аякс запросов одинакова, то имхо лучше всё-таки сократить усилия на экшен. Хотя и ваш подход имеет право на жизнь, не спорю.
Дело в том, что до написания статьи я действительно не рассматривал IsAjaxRequest(). Но не мене важно, что приведенный выше подход легка накладывается на POST запросы, а также неплохо разделяет логику ajax и полной страницы, как вы верно заметили. Это бывает очень полезно, а иногда — очень вредно =) Зависит от ситуации…
PartialOrFullView… Что-то не очень смотрится…
Лучше назвать базовый контроллер как-нибудь BaseAjaxController, и перегрузить сам View.
protected override ViewResult View(IView view, object model)
{
if (Request.IsAjaxRequest)
{
return PartialView(view,model);
}
return base.View(view, model);
}
Спасибо, полезное замечание!
На днях пересмотрю свое решение, и возможно тогда «магия», описанная в статье останется, лишь как пример перехвата запросов, а вся архитектура станет еще проще =)
А скажите пожалуйста, а ваш код работает во всех браузерах? Если нет, то в каких?
Можно, последний хром/сафари, последний лис. IE честно отказался работать с таким ajax =(
А, я так и думал, что прощай корпоративный рынок. Предсказуемо.

(я-то думал, мы что-то пропустили, когда исследовали решения для ie)
Проект не компилируется в VS 2010, выдаёт ошибку:

Error 1 Could not load file or assembly 'System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. Не удается найти указанный файл. F:\Projects\AjaxNavigation\Web.config 24
Осмелюсь предположить, что не установлен MVC 3.0… Других идей сходу нету.
А вы экстримал.

1) Про Request.IsAjaxRequest() выше уже написали.
2) Зачем выкалывать глаза студии удаляя vsdoc-файлы. Она же вам помочь хочет.
3) Ради совместимости может стоит использовать HistoryJS?

В принципе при применении первого пункта половина статьи сразу-же растает.

Мой одностраничный сайтик переписанный на MVC3 всего за ночь со всеми скриптами ocenka.tusur.ru/.

По статистике метрики менее 20% визитов используют возможности History API у остальных старые браузеры и History API эмулируется с помощью хешей.
В самом начале статьи упомянул про поддержку только современных браузеров, а в самом конце — про history-js =)
Во многом вы конечно правы. Однако, одно то, что мы подняли и обсуждаем тут эту проблему — очень неплохо… Если бы когда я начинал заниматься этим вопросом под рукой оказалась такая статья с комментариями — я бы очень много времени сэкономил =)
Есть несколько замечаний по коду:

1. Для эмуляции долговременной операции лучше не использовать конструкцию:

for (int i = 0; i < 1000000000; i++)
{ }

Лучше использовать метод Thread.Sleep из пространства имен System.Threading.

2. Не стоит хранить данные как содержимое в тэгах DIV c идентификаторами pageTitle и pageUrl.
Лучше использовать jQuery.data или на худой конец <input type="hidden" />.
Поигрался немного с примером, учел комменты. Вот что получается:
Для и IE делаем заглушку в JS — хотябы чтобы не падал скрипт.

if (!window.history.pushState) {
window.history.pushState = function () { }
}
if (!window.history.replaceState) {
window.history.replaceState = function () { }
}


Если использовать базовый класс в методом

protected ViewResultBase PartialOrFullView(string name = null, object model = null)
{
if (this.Request.IsAjaxRequest()) return PartialView(name, model);
return View(name, model);
}


то оверрайд на execute и урл для @Html.PageInfo не нужны. Для получения урла я использую такой код:

var url = VirtualPathUtility.ToAbsolute("~/"+
helper.ViewContext.RouteData.Route.GetVirtualPath(
helper.ViewContext.RequestContext,
helper.ViewContext.RouteData.Values).VirtualPath);
if (title == null) title = url;


Only those users with full accounts are able to leave comments. Log in, please.