MVC5中如何在Hangfire后台任务完成后发送通知?
在MVC5 + Hangfire中实现后台任务完成后的前端通知
这个场景太常见了——后台任务已经丢给Hangfire跑了,控制器早早就返回了JSON,没法再通过原请求给前端发完成通知对吧?别担心,这里有几个靠谱的解决方案,你可以根据自己的场景选:
方案一:用SignalR实现实时推送(最优雅的方式)
SignalR是ASP.NET的实时通信库,能让服务器主动给前端发消息,完美适配这种后台任务完成通知的场景。
步骤1:配置SignalR
- 先通过NuGet安装
Microsoft.AspNet.SignalR包 - 在
Startup.cs里注册SignalR中间件:
using Microsoft.Owin; using Owin; using Microsoft.AspNet.SignalR; [assembly: OwinStartup(typeof(YourProject.Startup))] namespace YourProject { public class Startup { public void Configuration(IAppBuilder app) { // 注册SignalR路由 app.MapSignalR(); // 其他现有配置... } } }
步骤2:创建通知Hub
新建一个Hub类,用来处理服务器到前端的消息推送:
using Microsoft.AspNet.SignalR; public class NotificationHub : Hub { // 给指定用户发送完成通知的方法 public void SendTaskCompleteNotification(string userId, string message) { // 如果是登录用户,可以用UserId定向推送;如果是匿名用户,也可以用ConnectionId Clients.User(userId).receiveTaskComplete(message); } }
步骤3:修改后台任务和控制器
更新你的后台任务,在任务完成后调用SignalR发送通知;同时控制器可以返回任务ID(可选,方便前端关联):
public ActionResult RunMedia(int ID) { // 获取当前登录用户的ID(如果是匿名场景,可以用SessionId或者前端传的标识) var userId = User.Identity.GetUserId(); var jobId = BackgroundJob.Enqueue<CrawlerSearchRequestModel>(x => x.StoreMedia(ID, "NaturalPerson", userId)); return Json(new { success = true, jobId = jobId }, JsonRequestBehavior.AllowGet); } // 修改你的CrawlerSearchRequestModel的StoreMedia方法 public void StoreMedia(int ID, string type, string userId) { // 原有的任务逻辑... // 任务完成后,调用SignalR发送通知 var hubContext = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>(); hubContext.Clients.User(userId).receiveTaskComplete($"媒体存储任务ID {ID} 已完成!"); }
步骤4:前端JavaScript连接SignalR
在页面里引入SignalR的脚本,连接Hub并监听通知:
<script src="/Scripts/jquery.signalR-2.4.3.min.js"></script> <script src="/signalr/hubs"></script> <script> $(function () { // 连接SignalR Hub var notificationHub = $.connection.notificationHub; // 定义接收通知的方法 notificationHub.client.receiveTaskComplete = function (message) { // 这里可以用弹窗、Toast等方式展示通知 alert(message); // 或者更新页面元素:$('#notification').text(message); }; // 启动连接 $.connection.hub.start().done(function () { console.log("已连接到通知Hub"); }); }); </script>
方案二:前端轮询检查任务状态(适合简单场景)
如果不想引入SignalR,也可以用前端定时查询的方式,虽然实时性稍差,但实现简单。
步骤1:控制器返回JobId
修改控制器,把Hangfire任务的JobId返回给前端:
public ActionResult RunMedia(int ID) { var jobId = BackgroundJob.Enqueue<CrawlerSearchRequestModel>(x => x.StoreMedia(ID, "NaturalPerson")); return Json(new { success = true, jobId = jobId }, JsonRequestBehavior.AllowGet); }
步骤2:新增API接口查询任务状态
添加一个控制器方法,用来查询指定JobId的任务状态:
public ActionResult CheckJobStatus(string jobId) { var jobDetails = JobStorage.Current.GetConnection().GetJobData(jobId); bool isCompleted = jobDetails.State == "Succeeded"; return Json(new { isCompleted = isCompleted }, JsonRequestBehavior.AllowGet); }
步骤3:前端轮询逻辑
在Ajax调用RunMedia成功后,开始定时查询任务状态:
$.ajax({ url: '/YourController/RunMedia', type: 'POST', data: { ID: 123 }, success: function (response) { if (response.success) { // 每5秒查询一次任务状态 var pollInterval = setInterval(function () { $.ajax({ url: '/YourController/CheckJobStatus', type: 'GET', data: { jobId: response.jobId }, success: function (statusResponse) { if (statusResponse.isCompleted) { clearInterval(pollInterval); alert("任务已完成!"); } } }); }, 5000); } } });
方案三:利用Hangfire延续任务(适合复杂任务链)
如果你的任务有依赖或者需要在完成后执行额外逻辑,可以用Hangfire的延续任务,把通知逻辑放到延续任务里:
public ActionResult RunMedia(int ID) { var userId = User.Identity.GetUserId(); var jobId = BackgroundJob.Enqueue<CrawlerSearchRequestModel>(x => x.StoreMedia(ID, "NaturalPerson")); // 添加延续任务,原任务完成后执行通知逻辑 BackgroundJob.ContinueWith(jobId, () => SendTaskCompleteNotification(userId, $"任务ID {ID} 完成")); return Json(new { success = true }, JsonRequestBehavior.AllowGet); } private void SendTaskCompleteNotification(string userId, string message) { var hubContext = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>(); hubContext.Clients.User(userId).receiveTaskComplete(message); }
内容的提问来源于stack exchange,提问作者Tebogo Langa
相关产品推荐
相关产品推荐

