Всем известна проблема с тем, что IE пытается кешированть больше чем нужно, и из-за этого возникают проблемы с ajax. Хочу рассказать, как решать эту проблему, используя связку ASP.NET MVC + jQuery. Итак суть задачи:
Браузер Internate Explorer кеширует обращения к серверу и из-за этого ajax-запросы возвращают некорректные результаты.
Варианты решения:
1 Использовать POST вместо GET
Считается, что IE кеширует только GET-запросы, поэтому достаточно переключиться на POST и всё заработает. На самом деле это не совсем так.
Во-первых, не всегда можно легко заменить GET на POST. Например, в ASP.NET MVC типичным является приём, когда создается пара методов — один 1 реагирует на GET и возвращяет разметку, другой 2 принимает POST и осуществляет некоторую операцию:
public class UserController
{
public ActionResult New() 1
{
return View();
}
[HttpPost]
public ActionResult New(UserForm form) 2
{
UserService.CreateNewUser(form);
return View();
}
}
Если через ajax требуется получить разметку из 1, то понятно, что на POST переключиться не получится.
Во-вторых, установлено, что IE кеширует даже POST-запросы в том случае, если POST не содержит никаких параметров.
2 Запретить кеширование на стороне сервера
Можно запретить кеширование на уровне сервера. В WebForms это делалось так:
Response.CacheControl = "no-cache";
Response.AddHeader("Pragma", "no-cache");
Response.Expires = -1;
Однако, некоторые утверждают, что такой способ для них не работает. В MVC можно использовать фильтр, например тот который сделал Jan Willem Boer:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace YourNameSpaceHere
public class BrowserCacheAttribute : ActionFilterAttribute
{
/// <summary>
/// Gets or sets the cache duration in seconds.
/// The default is 10 seconds.
/// </summary>
/// <value>The cache duration in seconds.</value>
public int Duration
{
get;
set;
}
public bool PreventBrowserCaching
{
get;
set;
}
public BrowserCacheAttribute()
{
Duration = 10;
}
public override void OnActionExecuted(
ActionExecutedContext filterContext)
{
if (Duration < 0) return;
HttpCachePolicyBase cache = filterContext.HttpContext
.Response.Cache;
if (PreventBrowserCaching)
{
cache.SetCacheability(HttpCacheability.NoCache);
Duration = 0;
}
else
{
cache.SetCacheability(HttpCacheability.Public);
}
TimeSpan cacheDuration = TimeSpan.FromSeconds(Duration);
cache.SetExpires(DateTime.Now.Add(cacheDuration));
cache.SetMaxAge(cacheDuration);
cache.AppendCacheExtension("must-revalidate,"
+ "proxy-revalidate");
}
}
}
Хотя этот подход самый прямой, у него тоже есть недостаток. В ASP.NET MVC один и тот же метод контроллера часто используется для получения и основной страницы и её части через ajax:
public ActionResult New()
{
if (Request.IsAjaxRequest())
{
return /* возвращаем ajax-результат */;
}
return /* возвращаем обычный результат */;
}
Если пометить метод New
атрибутом BrowserCacheAttribute
, то кеширование отменится и для обычных и для ajax-запросов. Это не совсем правильно. Нам нужно управлять только кешируемостью ajax-запроса, не трогая основную страницу. Вернее, нам нужно чтобы ajax-запрос возвращал всегда новый результат, даже если основная страница закеширована браузером.
3 Дополнительный параметр в запросе
Это самый действенный способ. Добавляем в список параметров какое-нибудь уникальное значение — и сам запрос также считается уникальным и не кешируется. В качестве уникального значения обычно используется timstamp:
$.ajax({
type: "GET",
url: "/users/list",
data: { tstamp: new Date().getTime() },
/* ... */
});
4 Использовать поддержку jQuery
Функция jQuery $.ajax в качестве одного из аргуметнов принимает параметр cache (Boolean)
. Если cache=false
, то jQuery автоматически добавит к запросу уникальный параметр (тот же timestamp). То есть приведенный выше код можно записать следующим образом:
$.ajax({
type: "GET",
url: "/users/list",
cache: false,
/* ... */
});
Тут подводный камень в том, что если используется POST вместо GET, то jQuery опускает этот самый уникальный параметр за (якобы) ненужностью. То есть, если запрос посылается через GET, то можно смело использовать cache: false
, но если запрос — это POST без параметров, то придется добавлять что-то типа data: { tstamp: new Date().getTime() }
.
Спасибо... Помогло !
Еще как получится, что вам мешает метод public ActionResult New() пометить атрибутом [HttpPost]? А в самом Ajax.BeginForm или Ajax.ActionLink указать AjaxOptions{HttpMethod = "POST"}?