我有奇怪的行为,无法在本地机器上复制,它开始让我发疯。
看起来 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
}
});
对于小订单,它工作正常,但对于大订单,会创建两个订单,原因似乎是处理时间,订单越大,将其放置到外部 Web 服务所需的时间就越多。
我检查了 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)有问题。