MVC 异步无法解释的流程
Unexplainable flow with MVC async
我对 .NET 异步任务的经验有限,但我过去使用过 BackgroundWorker 和 IAsyncResult。下面的代码在 MVC4 项目中。当没有错误时,一切正常。问题是什么时候失败了。异步调用似乎发生了一些意想不到的事情,我无法解释。
目前,SMTP 服务器设置为一个不存在的服务器,因此代码在对 await smtp.SendMailAsync(message);
的异步调用中爆炸。而不是调用邮件服务器生成错误,错误似乎来自用户创建过程中的某个地方 "The Name is already taken." 这绝对不是这种情况,因为任何随机输入的用户名都会导致消息(例如:"asdhadsgasgdjasdgj@sadasd.com").
我的期望:
- 用户已创建
- 已添加角色
- 发送电子邮件的调用失败,生成 'Connection refused to mail server' 错误。
实际发生了什么:
- 用户已创建
- 已添加角色
- 发送电子邮件的调用失败,但错误消息来自 IUserStore 中 .NET 的用户创建逻辑中的某处。
我在本地得到了预期的结果,但在服务器上我得到了奇怪的行为。
似乎导致错误的创建用户方法:
public virtual Task CreateAsync(User user)
{
if (user == null)
throw new ArgumentNullException("user");
return Task.Factory.StartNew(() =>
{
using (SqlConnection cn = new SqlConnection(DbHelper.ConnectionString))
{
cn.Insert<User>(user);
}
});
}
注册控制器中的动作:
[POST("register")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel vm)
{
if (ModelState.IsValid)
{
var user = AutoMapper.Mapper.Map<User>(vm);
user.PasswordDate = DateTime.Now;
var result = await _userManager.CreateAsync(user); // Error message seems to come from here
if (result.Succeeded)
{
// Add roles
//...
foreach (var area in vm.AllowedAreas.Where(a => a.Value))
await _userManager.AddClaimAsync(user.Id, new Claim("Area", area.Key));
// Generate an email and send it out
var thanksForRegisteringVm = new ThanksForRegisteringViewModel();
thanksForRegisteringVm.FullName = vm.FullName;
thanksForRegisteringVm.EmailAddress = vm.EmailAddress;
var emailBody = RenderViewAsString.Render("~/Views/Emails/ThanksForRegistering.cshtml", thanksForRegisteringVm);
var message = new MailMessage()
{
Subject = "Thanks for Registering",
Body = emailBody,
IsBodyHtml = true
};
message.To.Add(vm.EmailAddress);
var smtp = new SmtpClient();
await smtp.SendMailAsync(message); // this will definitely be the true source of the error - a non existant mail server in web.config
return RedirectToAction("ThanksForRegistering");
}
else
{
foreach (var error in result.Errors)
ModelState.AddModelError("", error);
}
}
// If we got this far, something failed, redisplay form
setUpRegisterViewModel(vm);
return View(vm);
}
更新: 我们已将其缩小到本地、开发和实时 IIS 配置中某处的差异。本地和开发工作,现场没有。我们已经消除的东西:
- 应用程序池(创建了一个新的)
- Elmah(已从直播中移除)
- applicationhost.config
无论控制器如何,GET 和 POST 请求都会调用操作方法两次,但 仅在 抛出异常时调用。好像第一个异常导致 action 方法再次执行。
在将站点复制到新项目并从 Nuget 安装所有依赖项后,我们最终将其固定到代理服务器 - 运行 Cisco IronPort。
可能是由于配置错误或基础架构团队有意设置的原因,代理服务器正在向服务器提交两个相同的请求,即使只有一个请求是从客户端发送的。
禁用代理服务器或从外部 (phone) 连接访问会产生一个请求。
我对 .NET 异步任务的经验有限,但我过去使用过 BackgroundWorker 和 IAsyncResult。下面的代码在 MVC4 项目中。当没有错误时,一切正常。问题是什么时候失败了。异步调用似乎发生了一些意想不到的事情,我无法解释。
目前,SMTP 服务器设置为一个不存在的服务器,因此代码在对 await smtp.SendMailAsync(message);
的异步调用中爆炸。而不是调用邮件服务器生成错误,错误似乎来自用户创建过程中的某个地方 "The Name is already taken." 这绝对不是这种情况,因为任何随机输入的用户名都会导致消息(例如:"asdhadsgasgdjasdgj@sadasd.com").
我的期望:
- 用户已创建
- 已添加角色
- 发送电子邮件的调用失败,生成 'Connection refused to mail server' 错误。
实际发生了什么:
- 用户已创建
- 已添加角色
- 发送电子邮件的调用失败,但错误消息来自 IUserStore 中 .NET 的用户创建逻辑中的某处。
我在本地得到了预期的结果,但在服务器上我得到了奇怪的行为。
似乎导致错误的创建用户方法:
public virtual Task CreateAsync(User user)
{
if (user == null)
throw new ArgumentNullException("user");
return Task.Factory.StartNew(() =>
{
using (SqlConnection cn = new SqlConnection(DbHelper.ConnectionString))
{
cn.Insert<User>(user);
}
});
}
注册控制器中的动作:
[POST("register")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel vm)
{
if (ModelState.IsValid)
{
var user = AutoMapper.Mapper.Map<User>(vm);
user.PasswordDate = DateTime.Now;
var result = await _userManager.CreateAsync(user); // Error message seems to come from here
if (result.Succeeded)
{
// Add roles
//...
foreach (var area in vm.AllowedAreas.Where(a => a.Value))
await _userManager.AddClaimAsync(user.Id, new Claim("Area", area.Key));
// Generate an email and send it out
var thanksForRegisteringVm = new ThanksForRegisteringViewModel();
thanksForRegisteringVm.FullName = vm.FullName;
thanksForRegisteringVm.EmailAddress = vm.EmailAddress;
var emailBody = RenderViewAsString.Render("~/Views/Emails/ThanksForRegistering.cshtml", thanksForRegisteringVm);
var message = new MailMessage()
{
Subject = "Thanks for Registering",
Body = emailBody,
IsBodyHtml = true
};
message.To.Add(vm.EmailAddress);
var smtp = new SmtpClient();
await smtp.SendMailAsync(message); // this will definitely be the true source of the error - a non existant mail server in web.config
return RedirectToAction("ThanksForRegistering");
}
else
{
foreach (var error in result.Errors)
ModelState.AddModelError("", error);
}
}
// If we got this far, something failed, redisplay form
setUpRegisterViewModel(vm);
return View(vm);
}
更新: 我们已将其缩小到本地、开发和实时 IIS 配置中某处的差异。本地和开发工作,现场没有。我们已经消除的东西:
- 应用程序池(创建了一个新的)
- Elmah(已从直播中移除)
- applicationhost.config
无论控制器如何,GET 和 POST 请求都会调用操作方法两次,但 仅在 抛出异常时调用。好像第一个异常导致 action 方法再次执行。
在将站点复制到新项目并从 Nuget 安装所有依赖项后,我们最终将其固定到代理服务器 - 运行 Cisco IronPort。
可能是由于配置错误或基础架构团队有意设置的原因,代理服务器正在向服务器提交两个相同的请求,即使只有一个请求是从客户端发送的。
禁用代理服务器或从外部 (phone) 连接访问会产生一个请求。