.NET Core的替代ThreadPool.QueueUserWorkItem的方法

3

我正在实现在ASP.NET Identity中使用AccountController的ForgotPassword功能,与标准的VS 2015项目模板相同。

我要解决的问题是,在发送密码重置电子邮件时,页面响应会有明显的延迟。如果密码恢复尝试未找到现有帐户,则不会发送电子邮件,因此响应更快。因此,我认为可以利用这种明显的延迟进行帐户枚举,即黑客可以根据忘记密码页面的响应时间确定帐户是否存在。

因此,我想消除页面响应时间上的差异,以便无法检测到是否找到了帐户。

过去,我使用了类似于以下代码的方法将潜在缓慢的任务(如发送电子邮件)排队到后台线程中:

ThreadPool.QueueUserWorkItem(new WaitCallback(AccountNotification.SendPasswordResetLink), 
notificationInfo);

但是在.NET Core中不存在ThreadPool.QueueUserWorkItem,因此我需要一些替代方案。

我想一个想法是在找不到账户的情况下引入人工延迟,使用Thread.Sleep,但我更希望找到一种发送电子邮件而不阻塞UI的方法。

更新:为了澄清问题,我发布实际代码:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{   
    if (ModelState.IsValid)
    {
    var user = await userManager.FindByNameAsync(model.Email);
    if (user == null || !(await userManager.IsEmailConfirmedAsync(user)))
    {
        // Don't reveal that the user does not exist or is not confirmed
        return View("ForgotPasswordConfirmation");
    }

    var code = await userManager.GeneratePasswordResetTokenAsync(user);
    var resetUrl = Url.Action("ResetPassword", "Account", 
        new { userId = user.Id, code = code }, 
        protocol: HttpContext.Request.Scheme);

    //there is a noticeable delay in the UI here because we are awaiting
    await emailSender.SendPasswordResetEmailAsync(
    userManager.Site,
    model.Email,
    "Reset Password",
    resetUrl);


        return View("ForgotPasswordConfirmation");
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}

有没有其他内置框架功能可以处理这个问题的好方法?


旁注:对于发送提醒电子邮件,这种解决方案是可以接受的,因为如果电子邮件没有发送成功,用户可以请求另一个。但一般情况下,在ASP.NET上将工作排队到线程池中不建议出于可靠性的原因。 - Stephen Cleary
谢谢@Stephen Cleary,我也在考虑在我的电子邮件代码中加入try catch,以确保如果后台线程发生错误,它不会使线程或进程崩溃。 - Joe Audette
错误处理是一个问题 - 默认情况下,如果您不等待任务,任何错误都会被静默忽略。但我更关心的是可靠性,因为在 ASP.NET 中与请求无关的任何工作都不能保证完成。 - Stephen Cleary
1
是的,对于需要更可靠的事情,可能希望使用类似 hangfire 或 azure web jobs 的东西,但是 hangfire 目前不支持 .NET Core,所以必须等待他们的支持。顺便说一下,感谢 @stephen-cleary 的精彩博客文章,您的文章是我在研究这些内容时找到的最好信息之一。 - Joe Audette
ThreadPool.QueueUserWorkItem 似乎一直存在于 Core 1.0 中。https://learn.microsoft.com/en-us/dotnet/api/system.threading.threadpool.queueuserworkitem?view=netcore-1.0 - xr280xr
1个回答

3
不要等待任务完成。这基本上等同于在线程池中运行所有代码,假设没有内部await调用ConfigureAwait(false)。(如果是你的代码,你需要检查这一点。)您可能希望将任务添加到某些任务集中,在服务器关闭之前必须等待这些任务完成,假设在ASP.NET中有适当的“请求关闭”概念。这值得研究,并且会防止由于不幸的时间错误而丢失通知,如立即在发送响应后但在发送通知前关闭服务器。但在发送通知时存在问题的情况下(例如,您的邮件服务器宕机),它并不能帮助解决问题。此时,用户已被告知电子邮件正在发送中,而您无法真正保证...这只是一个需要考虑的问题。

@JoeAudette:如果它已经是一个异步方法,那么调用应该很快;任何延迟都应该是异步的。如果不是这种情况,您应该重新审查实现 :) - Jon Skeet
@JoeAudette:啊,我明白了。那你可以选择不等待它……通常这是一个坏主意,因为你可以返回响应,然后电子邮件可能无法发送,或者机器可能会崩溃,但这是另一回事…… - Jon Skeet
这个问题也存在于ThreadPool.QueueWorkerItem中,我认为可能我不应该等待,除非出现错误,否则应该没问题。 - Joe Audette
@JoeAudette:不,情况并不比那更糟。我会更新的。 - Jon Skeet
谢谢!@JonSkeet 您的知识是传奇! - Joe Audette
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接