Вот поток:
- Микросервис получает запрос в контроллер, где заголовки (определяющие учетная запись — система и клиент).
- Контроллер вызывает типизированный HttpClient.
- HttpClient отправляет запрос сторонней службе.
HttpMessageHandler определяет информацию об учетной записи на основе полученного запроса и устанавливает заголовки для исходящего запроса. - Все запросы и ответы, включая информацию об учетной записи, передаются вошел в базу данных микросервиса.
Когда мы развернули микросервис в рабочей среде и начали обрабатывать несколько клиентов , мы столкнулись с проблемой параллелизма: данные для учетной записи X были зарегистрированы, но запрос к третьей стороне был обработан под учетной записью Y.
Чтобы разобраться в этой ситуации, я попробовал:
- Я зарегистрировал информацию заголовков (учетную запись) в HttpMessageHandler до и непосредственно после отправки запроса
- Я использую Serilog в качестве средства ведения журнала, поэтому я установил пакет NuGet serilog-httpclient и все записал в журнал.
- Я использовал одноэлементный экземпляр HttpClient непосредственно из контроллера и использовал его в одном из API.
Я не уверен, связана ли проблема с параллелизм в моем коде или что-то еще. В журналах постоянно отображаются правильные заголовки, но каким-то образом запрос доходит до стороннего сервиса с неверной информацией об учетной записи. Может ли это быть проблемой параллелизма в моей службе или проблема связана со сторонней службой?
Вот мой код:
Startup.cs
services.AddScoped();
services.AddScoped();
services.AddTransient();
services.AddHttpClient(c =>
{
c.BaseAddress = new Uri(configuration["MyAPIUrl"]);
})
.LogRequestResponse(p =>
{
p.LogMode = LogMode.LogAll;
p.RequestHeaderLogMode = LogMode.LogAll;
p.RequestBodyLogMode = LogMode.LogAll;
p.ResponseHeaderLogMode = LogMode.LogAll;
p.ResponseBodyLogMode = LogMode.LogAll;
p.MaskedProperties.Clear();
})
.AddHttpMessageHandler();
MyController.cs
public class MyController : MyControllerBase
{
private readonly MyAPI _myAPI;
public MyController(MyAPI myAPI)
: base(serviceProvider)
{
_myAPI = myAPI;
}
[HttpGet("Status")]
public async Task GetStatusAsync(int myId)
{
try
{
var response = await _myAPI.GetContractAsync();
//rest of the code
}
catch (Exception ex)
{
return PrepareFailedCallResult(ex);
}
}
}
MyAuthenticationMessagehandler.cs
public class MyAuthenticationMessageHandler : DelegatingHandler
{
private readonly IHttpContextAccessor _httpContextAccessor;
public MyAuthenticationMessageHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var accountService = _httpContextAccessor.HttpContext.RequestServices.GetRequiredService();
var accountInfo = await accountService.GetAccountInfo();
request.Headers.Add(APIKeys.APIId, accountInfo.APIId);
request.Headers.Add(APIKeys.APIKey, accountInfo.APIKey);
request.Headers.Add(APIKeys.APIAuthorizationToken, accountInfo.APIAuthorizationToken);
return await base.SendAsync(request, cancellationToken);
}
}
AccountService.cs
public class AccountService : ApplicationServiceBase, IAccountService
{
private readonly IRepository _accountRepository;
public AccountService(IRepository accountRepository, IMapper mapper, IServiceProvider serviceProvider) : base(mapper, serviceProvider)
{
_accountRepository = accountRepository;
}
public async Task GetAccountInfo()
{
Expression predicate =
account =>
account.TenantId == Session.TenantId &&
account.SystemId == Session.SystemId;
var account = (await _accountRepository.All(predicate)).Select(a => _mapper.Map(a)).FirstOrDefault();
if (account == null)
throw new Exception($"Account with TenantId:{Session.TenantId}, SystemId:{Session.SystemId} is invalid");
return account;
}
}
Подробнее здесь: https://stackoverflow.com/questions/790 ... ency-issue