I have been working on creating maintainable networking layer for our Asp.Net Core Web Api project. Right now I have created Generic methods for Get, Post, Put like this.
public class BaseClient
{
private HttpClient _client;
private ILogger<BaseClient> _logger;
private string AuthToken;
public BaseClient(HttpClient client, ILogger<BaseClient> logger, AuthenticationHeader authHeader)
{
_client = client;
client.BaseAddress = new Uri("url/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
_logger = logger;
AuthToken = authHeader.AuthHeader;
}
public async Task<FailureResponseModel> GetFailureResponseModel(HttpResponseMessage response)
{
FailureResponseModel failureModel = await response.Content.ReadAsAsync<FailureResponseModel>();
failureModel.ResponseStatusCode = Convert.ToInt32(response.StatusCode);
_logger.LogError("Request Failed: {Error}", failureModel.ResultDetails);
return failureModel;
}
public async Task<object> ProcessAsync<T>(HttpRequestMessage request, NamingStrategy namingStrategy)
{
if (!string.IsNullOrEmpty(AuthToken))
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken);
}
HttpResponseMessage response = await _client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
_logger.LogInformation("Request Succeeded");
var dezerializerSettings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = namingStrategy
}
};
T responseModel = JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync(), dezerializerSettings);
return responseModel;
}
else
{
return await GetFailureResponseModel(response);
}
}
public async Task<object> GetAsync<T>(string uri)
{
return await GetAsync<T>(uri, new DefaultNamingStrategy());
}
public async Task<object> GetAsync<T>(string uri, NamingStrategy namingStrategy)
{
using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri))
{
return await ProcessAsync<T>(requestMessage, namingStrategy);
}
}
public async Task<object> PostAsync<T1, T2>(string uri, T2 content)
{
return await PostAsync<T1, T2>(uri, content, new DefaultNamingStrategy());
}
public async Task<object> PostAsync<T1, T2>(string uri, T2 content, NamingStrategy namingStrategy)
{
using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, uri))
{
var json = JsonConvert.SerializeObject(content);
using (var stringContent = new StringContent(json, Encoding.UTF8, "application/json"))
{
requestMessage.Content = stringContent;
return await ProcessAsync<T1>(requestMessage, namingStrategy);
}
}
}
public async Task<object> PutAsyc<T1, T2>(string uri, T2 content)
{
return await PutAsyc<T1, T2>(uri, content, new DefaultNamingStrategy());
}
public async Task<object> PutAsyc<T1, T2>(string uri, T2 content, NamingStrategy namingStrategy)
{
using (var requestMessage = new HttpRequestMessage(HttpMethod.Put, uri))
{
var json = JsonConvert.SerializeObject(content,
Formatting.None,
new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
});
using (var stringContent = new StringContent(json, Encoding.UTF8, "application/json"))
{
requestMessage.Content = stringContent;
return await ProcessAsync<T1>(requestMessage, namingStrategy);
}
}
}
As it can be seen I have to write 2 Methods for Get, Post and Put due to SnakeCase and CamelCase Naming Strategies as some response consist of SnakeCase and some responses are in CamelCase
I have created ApiManager kinda class which calls above Class methods like this.
public class ClubMatasClient
{
private BaseClient _client;
private ILogger<ClubMatasClient> _logger;
private string AuthToken;
public ClubMatasClient(BaseClient client, ILogger<ClubMatasClient> logger, AuthenticationHeader authHeader)
{
_client = client;
_logger = logger;
AuthToken = authHeader.AuthHeader;
}
public async Task<object> GetShops(string category)
{
_logger.LogInformation("ClubMatas outgoing request: {RequestName}", nameof(GetShops));
return await _client.GetAsync<ShopsResponseModel>($"v2/shops?category={WebUtility.UrlEncode(category)}");
}
public async Task<object> PostLogin(LoginRequestModel form)
{
_logger.LogInformation("ClubMatas outgoing request: {RequestName}", nameof(PostLogin));
return await _client.PostAsync<LoginResponseModel, LoginRequestModel>("v2/login", form, new SnakeCaseNamingStrategy());
}
}
And this class in being injected in my Controllers and I am using this ApiManager like this to call api and return response.
public async Task<ActionResult<object>> GetShops([FromQuery(Name = "category")]string category)
{
var response = await _httpClient.GetShops(category);
return ParseResponse<ShopsResponseModel>(response);
}
ParseResponse is helper Generic method which either return SuccessModel (passed as T) or failure response Model.
protected ActionResult<object> ParseResponse<T>(object response)
{
if (response.GetType() == typeof(T))
{
return Ok(response);
}
else
{
return Error(response);
}
}
Now my question is as I am fairly new to C#/ASP.net Core , is my current flow/architecture is fine, and how can I make it more elegant and more flexible.
BaseClientclass threw me off; give me some time and I'll supply a proper refactor as an answer. \$\endgroup\$