1. 程式人生 > >Core中使用Hangfire 在Asp.Net Core中使用DI的方式使用Hangfire構建後臺執行指令碼 解決 ASP.NET Core Hangfire 未授權(401 Unauthorized)

Core中使用Hangfire 在Asp.Net Core中使用DI的方式使用Hangfire構建後臺執行指令碼 解決 ASP.NET Core Hangfire 未授權(401 Unauthorized)

 

  之前使用Quartz.Net,後來發現hangfire對Core的繼承更加的好,而且自帶管理後臺,這就比前者好用太多了。

安裝註冊

安裝

PM> Install-Package Hangfire

Startup.cs,在ConfigureServices方法中添加註冊:

services.AddHangfire(x => x.UseSqlServerStorage("connection string"));

 

SqlServer是使用這種方式,其他方式見官方的文件及相應外掛。

註冊完成後,還需要在Configure方法中,新增如下高亮部分的程式碼:

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler(
"/Home/Error"); } //新增Hangfire app.UseHangfireServer(); app.UseHangfireDashboard();
配置完畢後執行我們的專案,這時Hangfire會自動在資料庫中建立結構,資料庫中會新建如下表:

現在在網站根目錄+/Hangfire即可檢視管理後臺介面如下:

image

基本使用

Hangfire的使用非常簡單,基本上使用以下幾個靜態方法:

//執行後臺指令碼,僅執行一次
BackgroundJob.Enqueue(() => Console.WriteLine("
Fire-and-forget!")); //延遲執行後臺指令碼呢,僅執行一次 BackgroundJob.Schedule( () => Console.WriteLine("Delayed!"), TimeSpan.FromDays(7)); //週期性任務 RecurringJob.AddOrUpdate( () => Console.WriteLine("Recurring!"), Cron.Daily); //等上一任務完成後執行 BackgroundJob.ContinueWith( jobId, //上一個任務的jobid () => Console.WriteLine("Continuation!"));

 

依賴注入

在.Net Core中處處是DI,一不小心,你會發現你在使用Hangfire的時候會遇到各種問題,比如下列程式碼:

public class HomeController : Controller
{
    private ILogger<HomeController> _logger;
    public HomeController(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<HomeController>();
    }
    public IActionResult Index()
    {
        _logger.LogInformation("start index");
        BackgroundJob.Enqueue(() => _logger.LogInformation("this a job!"));
        return View();
    }

}

 

專案啟動後,你能正常訪問,但在Hangfire後臺你會看到如下錯誤:

image
錯誤資訊呢大概意思是不能使用介面或者抽象方法類,其實就是因為Hangfire沒有找到例項,那如何讓Hangfire支援DI呢?

我們先建立一個MyActivator類,使其繼承Hangfire.JobActivator類,程式碼如下:

public class MyActivator : Hangfire.JobActivator
{
    private readonly IServiceProvider _serviceProvider;
    public MyActivator(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider;

    public override object ActivateJob(Type jobType)
    {
        return _serviceProvider.GetService(jobType);
    }
}

 

重寫了ActivateJob方法,使其返回的型別從我們的IServiceProvider中獲取。

我們試著寫兩個後臺指令碼,CheckService和TimerService,CheckService的Check方法在執行計劃時,會再次呼叫Hangfire來定時啟動TimerService:

CheckService:

public interface ICheckService
{
    void Check();
}
public class CheckService : ICheckService
{
    private readonly ILogger<CheckService> _logger;
    private ITimerService _timeservice;
    public CheckService(ILoggerFactory loggerFactory,
        ITimerService timerService)
    {
        _logger = loggerFactory.CreateLogger<CheckService>();
        _timeservice = timerService;
    }

    public void Check()
    {
        _logger.LogInformation($"check service start checking, now is {DateTime.Now}");
        BackgroundJob.Schedule(() => _timeservice.Timer(), TimeSpan.FromMilliseconds(30));
        _logger.LogInformation($"check is end, now is {DateTime.Now}");
    }
}

 

TimerService:

public interface ITimerService
{
    void Timer();
}
public class TimerService : ITimerService
{
    private readonly ILogger<TimerService> _logger;
    public TimerService(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<TimerService>();
    }
    public void Timer()
    {
        _logger.LogInformation($"timer service is starting, now is {DateTime.Now}");
        _logger.LogWarning("timering");
        _logger.LogInformation($"timer is end, now is {DateTime.Now}");
    }
}

 

目前還無法使用,我們必須在Startup中註冊這2個service:

services.AddScoped<ITimerService, TimerService>();
services.AddScoped<ICheckService, CheckService>();

 

我們在HomeController修改以下:

public IActionResult Index()
{
    _logger.LogInformation("start index");
    BackgroundJob.Enqueue<ICheckService>(c => c.Check());
    return View();
}

 

好,一切就緒,只差覆蓋原始的Activator了,我們可以在Startup.cs中的Configure方法中使用如下程式碼:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
{
    GlobalConfiguration.Configuration.UseActivator<MyActivator>(new MyActivator(serviceProvider));
    ……
    ……
}

預設情況下Configure方法時沒有IServiceProvider引數的,請手動新增

再次啟動,我們的Job就會成功執行,截圖如下:
image

補充:以上在開發環境可以正常使用,一旦釋出到正式環境會報401 Unauthorized未授權錯誤,原因是 Hangfire 預設增加了授權配置。

解決方式:

增加CustomAuthorizeFilter

public class CustomAuthorizeFilter : IDashboardAuthorizationFilter
{
    public bool Authorize([NotNull] DashboardContext context)
    {
        //var httpcontext = context.GetHttpContext();
        //return httpcontext.User.Identity.IsAuthenticated;
        return true;
    }
}

 

Configure增加配置:

app.UseHangfireDashboard("/hangfire", new DashboardOptions() { 
    Authorization = new[] { new CustomAuthorizeFilter() }
});

 

參考資料