using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Caching.Memory; using Newtonsoft.Json; namespace Website.Data { public abstract class ApiClient { private readonly HttpClient _client; private readonly IMemoryCache _cache; private readonly CacheDurations _cacheDurations; protected ApiClient(HttpClient client, IMemoryCache cache, CacheDurations cacheDurations) { _client = client; _cache = cache; _cacheDurations = cacheDurations; } private IDictionary ParseQueryParameters(object query) { var type = query.GetType(); var props = type.GetProperties(); return props.ToDictionary(info => info.Name, info => info.GetValue(query, null).ToString()); } private async Task DoCachedCall(HttpMethod method, string callerName, string url, Func> call) { if (method == HttpMethod.Post) return await call(); var baseKey = GetType().Name + ":" + callerName; var key = baseKey + "/" + url; return await _cache.GetOrCreate(key, async entry => { entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(_cacheDurations[baseKey]); return await call(); }); } private async Task Send(HttpMethod method, string url, object query, string callerName, HttpContent content = null) { if (query != null) url = QueryHelpers.AddQueryString(url, ParseQueryParameters(query)); return await DoCachedCall(method, callerName, url, async () => { using var httpRequest = new HttpRequestMessage(method, url) { Content = content }; var response = await _client.SendAsync(httpRequest); if (!response.IsSuccessStatusCode) throw new ApiCallException(response); var json = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject(json); }); } protected async Task Post(string url, object value, object query = null, [CallerMemberName] string callerName = "") { var json = JsonConvert.SerializeObject(value); using var requestBody = new StringContent(json, Encoding.UTF8, "application/json"); return await Send(HttpMethod.Post, url, query, callerName, requestBody); } protected async Task Get(string url, object query = null, [CallerMemberName] string callerName = "") { return await Send(HttpMethod.Get, url, query, callerName); } } }