У меня странное поведение, которое не может быть воспроизведено на локальной машине, и это начинает сводить меня с ума.
Похоже, что ASP.NET MVC пытается выполнить действие, что-то истекает по тайм-ауту, происходит сбой без каких-либо исключений и уведомление клиента ajax, затем попытка повторного запуска действия, клиент ajax получает ответ, но не от исходного вызова.
У меня есть действие контроллера:
[ValidateAntiForgeryToken]
[ClientErrorHandler]
public virtual ActionResult PlaceOrder(CheckoutDto checkoutDto)
{
LoadDataFromDatabase();
// this may take up to couple minutes
var orderConfirmationData = PlaceOrderToExternalWebservice();
SaveConfirmationData(orderConfirmationData);
return View(Transform(orderConfirmationData))
}
И я вызываю это с помощью jquery ajax:
$.ajax({
url: placeOrderActionUrl,
type: "POST",
async: true,
dataType: "html",
data: $('#checkoutForm').serialize(),
success: function (data) {
// show confirmation data
},
error: function (request, status, error) {
// show error message
}
});
И для небольших заказов работает нормально,но для больших заказов создаются два заказа, и причина, по-видимому, заключается во времени обработки, чем больше заказ, тем больше времени занимает его размещение на внешнем веб-сервисе.
Я проверил журналы IIS, чтобы убедиться, что клиентский скрипт не вызывает действие дважды -, а журналы IIS показывают только один вызов определенного действия.
Внешняя служба не дает сбоев, в журнале событий/журнале sql исключений нет.
Чтобы убедиться, что ajax-клиент получает ответ не от исходного вызова, я сделал блокировку:
[ValidateAntiForgeryToken]
[ClientErrorHandler]
public virtual ActionResult PlaceOrder(CheckoutDto checkoutDto)
{
try
{
if (OrderingLockedForCurrentUser())
{
Log("Locked");
return View("Already placing order");
}
LockOrderingForCurrentUser();
LoadDataFromDatabase();
// this may take up to couple minutes
var orderConfirmationData = PlaceOrderToExternalWebservice();
SaveConfirmationData(orderConfirmationData);
return View(Transform(orderConfirmationData))
}
finally
{
RemoveOrderingLockForCurrentUser();
}
}
И вместо того, чтобы возвращать подтвержденные данные, он возвращает «уже размещенный заказ».
Я думал, может быть, тайм-ауты выполнения действий, но я попробовал просто так
<httpRuntime executionTimeout="600" />
не помогло.
Есть идеи, где искать причину, что дополнительно проверять, чтобы включить какое-то дополнительное логирование?
Обновление :Интересно, что исходный вызов также завершен.
Обновление 2 :Я добавил фильтр действий AjaxOnly, чтобы быть уверенным, что он вызывается только из javascript:
public class AjaxOnlyAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAjaxRequest())
{
throw new Exception("This action is intended to be called from Ajax only.");
}
}
}
А из логов вызывается только из javascript, так что тайна продолжается...
Обновление 3:
Я изолировал проблему от простого сна потока в отдельном тестовом контроллере :
[ValidateAntiForgeryToken]
[AjaxOnly]
[ClientErrorHandler]
public virtual ActionResult PlaceOrderAction(CheckoutDto checkoutDto)
{
try
{
if (CanPlaceOrder(Request.RequestContext.HttpContext))
{
Thread.Sleep(TimeSpan.FromSeconds(90));
return Content("First time");
}
return Content("Second time");
}
finally
{
HttpContext.Cache.Remove(GetKey(userService.CurrentUser.UserId));
}
}
public bool CanPlaceOrder(HttpContextBase httpContext)
{
var userId = userService.CurrentUser.UserId;
var key = GetKey(userId);
if (httpContext.Cache[key] == null)
{
httpContext.Cache.Add(key, userId, null, DateTime.Now.AddMinutes(10), new TimeSpan(), CacheItemPriority.High, null);
return true;
}
return false;
}
private static string GetKey(int userId)
{
return "PlacingOrder{0}".With(userId);
}
. Пока он работает нормально на двух независимых машинах разработки (win 7 )и промежуточной машине в ec2 (win2008sp2 ), это почти наверняка проблема с настройками IIS производственного сервера (win 2008R2 x64 sp1 ).