首页 > 解决方案 > 使用 Hangfire 进行 Microsoft Graph API 调用

问题描述

我正在努力使用 Hangfire 后台作业延迟进行图形 API 调用,因为我相信 HttpContext 在进行调用时为空,我不知道如何解决这个问题。

// 设置作业延迟触发

BackgroundJob.Schedule(() => this.HangfireTest("test@gmail.com", false), TimeSpan.FromSeconds(20));

// 进行图形 api 调用,它会返回“尚未计算”的结果

var userInfo = this.graphServiceClient.Me.Request().Select(Info.Fields).GetAsync();

我试过在上下文中传递,但这不起作用。不知道如何解决这个问题。谢谢!

标签: c#microsoft-graph-apihangfire

解决方案


这是一个为我工作的 ASP.Net Core 3.1 MVC 原型实现。我在寻找解决方案时也遇到了很多问题,因此在这里发布。我已经编辑了一些代码,以便更容易阅读和删除解决此问题所需的臃肿。

请注意,该方法是从 GraphAPI 检索访问令牌并将其传递给后台作业,创建一个新的独立 GraphClient 实例并添加包含访问令牌所需的承载标头。

在 startup.cs 中,EnableTokenAcquisition 允许在控制器中检索访问令牌。

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(this.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
    .AddMicrosoftGraph(this.Configuration.GetSection("DownstreamApi"))
    .AddInMemoryTokenCaches();

控制器初始化...

using Microsoft.Graph;
using Microsoft.Identity.Web;

private readonly GraphServiceClient graphServiceClient;
private readonly ITokenAcquisition tokenAcquisition = null;

public HomeController(ILogger<HomeController> logger, IConfiguration iconfiguration, GraphServiceClient graphServiceClient, ITokenAcquisition tokenAcquisition)
{
    this.graphServiceClient = graphServiceClient;
    this.tokenAcquisition = tokenAcquisition;
}

计划作业控制器操作

public IActionResult ScheduleJob(BackgroundJobsViewModel model)
{
    try
    {
        string accessToken = string.Empty;

        try
        {
            accessToken = Task.Run(async () => await this.tokenAcquisition.GetAccessTokenForUserAsync(scopeList)).Result;
        }
        catch (MicrosoftIdentityWebChallengeUserException ex)
        {
            Task.Run(async () => await this.tokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeaderAsync(scopeList, ex.MsalUiRequiredException));
        }
        catch (Microsoft.Identity.Client.MsalUiRequiredException ex)
        {
            Task.Run(async () => await this.tokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeaderAsync(scopeList, ex));
        }

        if (!string.IsNullOrEmpty(accessToken))
        {

            // Recurring Job.
            RecurringJob.AddOrUpdate(job.JobName, () => UpdateUsersBackroundJob.UpdateUsers(this.currentUser.Account, accessToken), $"{seconds} {minutes} {hours} {day} {month} {week}");
        }
        else
        {
            return this.View("BackgroundJobs", model);
        }
    }
    catch (Exception ex)
    {
        var errorModel = this.HandleException(ex);
        return this.View("Error", errorModel);
    }
}

后台作业类和方法...注意 Info.Fields 我没有显示。

using Microsoft.Graph;
using Microsoft.Identity.Web;

/// <summary>
/// Class to encapsulate the background job to update users.
/// </summary>
public class UpdateUsersBackroundJob
{
    /// <summary>
    /// Gets or sets the Graph Client.
    /// </summary>
    private static GraphServiceClient GraphClient { get; set; }

    public static void UpdateUsers(string upnName, string graphApiAccessToken)
    {
        if (string.IsNullOrEmpty(graphApiAccessToken))
        {
            throw new ApplicationException("Cannot run the update users background job without specifying an access token first.");
        }

        // Create our own new graph client using the provided token.
        GraphClient = new GraphServiceClient(new DelegateAuthenticationProvider((requestMessage) =>
        {
            requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", graphApiAccessToken);
            return Task.FromResult(0);
        }));
        
        try
        {

                // Update DB User data from GraphAPI Azure AD
                var userInfo = Task.Run(async () => await GraphClient.Me.Request().Select(Info.Fields).GetAsync()).Result;
                
                ...do something with userInfo in application.
            }
            catch
            {
                ...Handle exception.
            }
        }
    }
}

推荐阅读