I have an API middle layer that captures requests, generates a token, and then passes that request on to the intended API endpoint. Only one token should be created at a time, so each request needs to check if a valid token exists and use it, or create a new one. If the middle layer API gets multiple requests at the same time, it should still only create one token.
To do this I am using SemaphoreSlim:
Controller that takes in a request to check for the current version of something:
[ApiController]
[Route("[controller]")]
[AuthorizationKey]
public class Controller : ControllerBase
{
private readonly ILogger<Controller> Logger;
private readonly Services Services;
public Controller(ILogger<Controller> logger, Services services)
{
Logger = logger;
Services = services;
}
[HttpGet]
[Route("version")]
[ResponseCache(NoStore = true, Duration = 0, Location = ResponseCacheLocation.None)]
public async Task<ActionResult> Version()
{
try
{
var result = await Services.GetVersionAsync();
var response = result.Content.ReadAsStringAsync();
if (result.StatusCode == HttpStatusCode.OK)
{
return Ok(response.Result);
}
else if (result.StatusCode == HttpStatusCode.Unauthorized)
{
return StatusCode(401, response.Result);
}
else
{
return StatusCode(500, response.Result);
}
}
catch (Exception ex)
{
return StatusCode(500);
}
}
}
Here is the class in my services:
public class Services
{
private readonly ILogger<Services> Logger;
private readonly IConfiguration Configuration;
private readonly IHttpClientFactory HttpClientFactory;
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
public Services(ILogger<Services> logger, IConfiguration configuration, IHttpClientFactory httpClientFactory)
{
Logger = logger;
Configuration = configuration;
HttpClientFactory = httpClientFactory;
}
public async Task<HttpResponseMessage> GetVersionAsync()
{
var httpClient = await CreateHttpClient();
var response = await httpClient.GetAsync(Configuration["VERSION_ENDPOINT"]);
return response;
}
#region Helpers
private async Task<HttpClient> CreateHttpClient()
{
var token = await GetAccessTokenAsync();
var httpClient = HttpClientFactory.CreateClient();
httpClient.BaseAddress = new Uri(Configuration["API_URI"]);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return httpClient;
}
private async Task<string> GetAccessTokenAsync()
{
await _semaphore.WaitAsync();
try
{
//Checks for a token in a table and returns it if it is valid, or gets a new one if one does not exist
/if no token, then it calls RequestAccessTokenAsync()
}
finally
{
_semaphore.Release();
}
}
private async Task<TokenResponse> RequestAccessTokenAsync()
{
//Gets a new token from a service
}
#endregion
}
If I submit a multiple requests at the same time it is creating multiple tokens. It's not always a 1 to 1 with how many requests I submit. For example I have done 2 requests and it created 2 tokens, 3 requests and it created 2 tokens, and now I just did 10 requests and it created 7 tokens, but either way I only want 1 valid token to be created at a time.
CodePudding user response:
Switching to static for the following code resolved the issue. Thank you again to user "Deleted" for the help.
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);