Provides a Standardised Rest Client that can be used in all Citrus-Lime projects.
$ dotnet add package CitrusLime.Core.RestProvides a set of generic & Cloud POS specific functionality for performing REST requests, there is a quick start guide below and then a more comprehensive set of examples, finally there is a version change log at the bottom of this readme.
For example make a POST call to https://example.com/api/account?isNew=true and return a strongly typed object from the Json returned
Register the service in the DI engine
services.AddRestClient();
Example call:
public class Example
{
private readonly IRestRequestService requestService;
public Example(IRestRequestService requestService)
{
this.requestService = requestService;
}
public async Task<Account> CreateAccount(string authToken, Account account)
{
IRestRequestBuilder requestBuilder = RestRequestBuilder
.Initialise()
.SetMethod(HttpMethod.Post)
.SetUrl(
"https://example.com/api/account",
new Dictionary<string, object>()
{
{ "isNew", true },
})
.AddHeader("Authorization", $"Bearer {authToken}")
.SetJsonBody(account);
using (RestRequestModel request = requestBuilder.Build())
using (RestResponseModel<Account> response = await this.requestService.GetResponseAsync<Account>(request))
{
return response.Body;
}
}
}
services.AddRestClient(); // for generic request service
services.AddCloudPosRestClient(); // for Cloud POS specific request service
services.AddRestClient(TimeSpan.FromSeconds(30));
services.AddCloudPosRestClient(TimeSpan.FromMinutes(2));
public class Example
{
private readonly IRestRequestService requestService;
public Example(IRestRequestService requestService)
{
this.requestService = requestService;
}
public async Task<Account> CreateAccount(Account account)
{
IRestRequestBuilder requestBuilder = RestRequestBuilder
.Initialise()
.SetMethod(HttpMethod.Post)
.SetUrl(
"https://example.com/api/account",
new Dictionary<string, object>()
{
{ "isNew", true },
})
.AddHeader("ApiKey", "abc123")
.SetTimeOut(30)
.SetJsonBody(account);
using (RestRequestModel request = requestBuilder.Build())
using (RestResponseModel<Account> response = await this.requestService.GetResponseAsync<Account>(request))
{
return response.Body;
}
}
}
public class Example
{
private readonly ICloudPosRequestService requestService;
public Example(ICloudPosRequestService requestService)
{
this.requestService = requestService;
}
public List<Account> GetAccounts(int id)
{
ICloudPosRequestBuilder requestBuilder = CloudPosRequestBuilder
.Initialise()
.SetMethod(HttpMethod.Get)
.SetPageSize(500)
.SetUrl(
$"https://example.com/api/account/{id}",
new Dictionary<string, object>()
{
{ "isActive", true },
{ "minAge", 1 },
})
.SetAuth("abc123");
using (RestRequestModel request = requestBuilder.Build())
using (RestResponseModel<List<Account>> response = await this.requestService.GetPagedResponseAsync<Account>(request))
{
return response.Body;
}
}
}
Get the response body as a string when a none 200 code occurs.
using (RestResponseModel<Account> response = await this.requestService.GetResponseAsync<Account>(request))
{
if (response.IsSuccessful == false)
{
Logger.LogError("Error: {Error}", response.ErrorBody);
}
}
public class ErrorModel // This is the error object returned by the API when a none 200 code occurs
{
public string Message { get; set; }
public string Code { get; set; }
}
using (RestResponseModel<Account> response = await this.requestService.GetResponseAsync<Account>(request))
{
if (response.IsSuccessful == false)
{
// Get the error object as a strongly typed object
ErrorModel error = response.GetErrorBody<ErrorModel>();
Logger.LogError("Error: {Error}", error.Message);
}
}
All notable changes to this project will be documented below.
System.Net.Http.HttpClient to be Warning.CloudPosRequestService and RestRequestService causing HttpContent to be disposed in some use cases.CloudPosRequestService.PerformRateLimitedResponse using an async call internally.AbstractRequestService requests.CloudPosRequestService rate-limited request logging.CloudPosRequestService requests.RestRequestService was being used in a synchronous manner.
.GetAwaiter().GetResult() method being called on non-system calls.CloudPosRequestService rate-limited request logging.Added the ability to get the response body when a none 200 code occurs.
using (RestResponseModel<Account> response = await this.requestService.GetResponseAsync<Account>(request))
{
if(response.IsSuccessful == false)
{
Logger.LogError("Error: {Error}", response.ErrorBody);
}
}
Added the ability to get a typed Error object when a none 200 code occurs. For example this API returns Json error objects when a call fails. https://developer.fiskaly.com/api/management/v0#operation/createOrganization
public class ErrorModel // The defined by the Json the API returns when an error occurs
{
public string Message { get; set; }
public string Code { get; set; }
}
using (RestResponseModel<Account> response = await this.requestService.GetResponseAsync<Account>(request))
{
if(response.IsSuccessful == false)
{
// Get the error object as a strongly typed object
ErrorModel error = response.GetErrorBody<ErrorModel>();
Logger.LogError("Error: {Error}", error.Message);
}
}
Changed the setting of the Authorisation header for the Cloud POS API request builder to be optional.
Fixed dependency injection registration method argument not being optional.
This major release creates a new method of wrapping the REST client, in order to provide a better dependency injection pattern for consuming this underlying client.
Added changes to enable publishing to NuGet.org.
Added fix for non-successful GET request returning blank response body. Removed dependencies on CitrusLime.Core.* NuGets to allow for moving to public repo/NuGet feed.
Implemented changes, aimed at improving the performance of the Cloud POS API client.
Replaced use of Threading.Sleep() with Task.Delay().
Updated the paging method to use a while-loop, instead of recursion.
Replaced use of .Result with .GetAwaiter().GetResult(). This means that the original exception is thrown, instead of an AggregateException.
The NuGet will now contain a set of DLLs for the Target Framework of NetStandard 2.1. This will allow it to be consumed by projects based on .Net5.
All the Build pipeline definitions have been updated to be consistent with the CitrusLime.Core project NuGets pipelines.
This will produce a pre-release on a successful push in the the Development branch and a stable release from a successful push in to the Release branch.
Update the Readme.md file to follow the same formatting as the others in the CitrusLime.Core project.