在半长异步请求期间处理不耐烦的用户

时间:2018-01-24 13:44:35

标签: c# asp.net-web-api async-await identityserver4

我们在.NET Core上运行IdentityServer4。目前用户使用他们的AzureAD帐户登录 - 如果在数据库中找不到用户名,那么我们查询Microsoft的Graph API以获取他们的组,一般信息等。

最近,一个应用程序几乎背靠背地发出了两个登录请求 - 由于用户不存在,它创建了查询并将其帐户添加到数据库两次

我一直在寻找解决这个问题的最佳方法,但从我所读到的内容来看,所有解决方案都被认为是不好的做法。

设置是这样的 - 登录后,用户被定向到AccountController - ExternalLoginCallback:

public async Task<IActionResult> ExternalLoginCallback(string returnUrl)
{
....
var user = await _users.AutoProvisionUser(provider, userId, externalUser, access_token, _options); // _users is the UserStore class
...
}

AutoProvisionUser看起来像这样:

public async Task<User> AutoProvisionUser(string provider, string userId, ClaimsPrincipal principal, string accessToken, Globals globals)
        {

            var user = await GetFullUserByUsernameAsync(principal.FindFirstValue(ClaimConstants.UPN));

            if (user.Account == null)
            {
                user = await CreateUserAsync(principal, userId, accessToken, globals.AzureApplication);
            }
            else
            {
                await UpdateGroupsAsync(user.Account, accessToken, globals.AzureApplication);
            }

            return new User
            {
                Claims = principal.Claims.ToList(),
                Name = user.Profile.DisplayName,
                Provider = provider,
                SubjectId = user.Account.AzureId,
                Username = user.Account.UserName
            };

        }

public async Task<UserIdentity> CreateUserAsync(ClaimsPrincipal principal, string userId, string graphAccessToken,
            AzureApplication options)
        {
        // API calls to Microsoft, adding them to the database and then returning the user...
        }

我一直在考虑制作一个方法来排队所有任务并等待它们在继续之前完成 - 这会解决问题还是另一个请求只是启动新线程并忽略当前执行?

我一直在考虑添加自定义 DelegateHandler ,并尝试在那里使用一些逻辑来查看我是否能够过滤掉“重复”请求。

我绝对最好的假设是,如果发出另一个重复请求,应用程序会在执行 user.Account == null之前等待当前正在运行的任何 CreateUserAsync 任务检查。实现是否切实可行?如果是这样,我可以通过包装上面提到的任务来实现吗?

1 个答案:

答案 0 :(得分:3)

任何形式的内存中队列或处理程序都只是一个创可贴,因为当您需要扩展到第二个Web服务器时会发生什么?

正确的解决方案是在最低级别执行此操作;即,在您的数据库中。对数据库的第二次写入将失败,此时您的代码可能会重新尝试其读取。这是optimistic concurrency的一种形式。