I have the following code to generate token.
How can I ensure to re-use the token if it is already generated. The token is used for subsequent API calls.
using (var client = new HttpClient())
{
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("username", _user));
postData.Add(new KeyValuePair<string, string>("password", _pwd));
postData.Add(new KeyValuePair<string, string>("grant_type", "password"));
postData.Add(new KeyValuePair<string, string>("client_id", _clientId));
postData.Add(new KeyValuePair<string, string>("client_secret", _clientSecret));
HttpContent content = new FormUrlEncodedContent(postData);
content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
var responseResult = client.PostAsync(_tokenUrl, content).Result;
return responseResult.Content.ReadAsStringAsync().Result;
}
CodePudding user response:
The token is valid for 1 hour. So you just need to store it for 1 hour before obtaining a new token. Ideally you'd get a token a few minutes before the time limit is up, to decrease the chance that you'll use an invalid token.
So, let's create a class responsible for obtaining and caching a token. This class needs to be registered with your Dependency Injection container as a singleton, so that there's only a single instance. Otherwise you'll needlessly get new tokens all the time. I don't know what Dependency Injection container you're using, so you'll have to research how to register our TokenClient class as a singleton with it.
We'll capture the time we obtained the token in a DateTime field, and the token itself in a field as well.
public class TokenClient
{
readonly IHttpClientFactory _httpClientFactory;
string _cachedToken;
DateTime _tokenRetrievalTime;
public TokenClient(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<string> GetToken()
{
if (string.IsNullOrEmpty(_cachedToken) || _tokenRetrievalTime is null || _tokenRetrievalTime.AddMinutes(55) > DateTime.Now))
{
_cachedToken = await GetTokenFromServer();
_tokenRetrievalTime = DateTime.Now;
return _cachedToken;
}
else
{
return _cachedToken;
}
}
async Task<string> GetTokenFromServer()
{
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("grant_type", "password"));
postData.Add(new KeyValuePair<string, string>("client_id", _clientId));
postData.Add(new KeyValuePair<string, string>("client_secret", _clientSecret));
HttpContent content = new FormUrlEncodedContent(postData);
content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
var client = _httpClientFactory.CreateClient();
var responseResult = await client.PostAsync(_tokenUrl, content);
string token = await responseResult.Content.ReadAsStringAsync();
return token;
}
}
There's a couple of things I'm doing differently here besides just caching the token. I'm using IHttpClientFactory to get an instance of an HttpClient, to avoid the issues detailed in You're Using HttpClient Wrong And It's Destablizing Your Software and You're probably still using HttpClient Wrong And It's Destabilizing Your Software. An even better solution would be to use Flurl, which takes care of those details for you.
The other thing I've changed compared to your code is that I'm properly awaiting the calls that return tasks. That keeps the code from deadlocking and helps keep it running efficiently. For more info on that, see Async Await Best Practices and 8 Await Async Mistakes You Should Avoid.