The second letter in the Hebrew alphabet is the ב bet/beit. Its meaning is "house". In the ancient pictographic Hebrew it was a symbol resembling a tent on a landscape.
Note: Pre-release packages are distributed via feedz.io.
This library extends various MessageHandlers
for HttpClient
.
If you like or are using this project to learn or start your solution, please give it a star. Thanks!
dotnet add package Bet.Extensions.Resilience.MessageHandlers
This library extends the HttpClient
timeout property by making a distinction between client canceling the request or the server.
This handler generates cookies per subsequently requests.
Authorization Handler allows to create an HttpClient
that automatically retrieve access token.
- Registration of the client for TrustPilot Api.
using System.Net.Http.Headers;
using System.Text;
using Bet.Extensions.Http.MessageHandlers.Authorize;
using Marketing.TrustPilot;
using Marketing.TrustPilot.Models;
using Marketing.TrustPilot.Options;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection;
public static class TrustPilotServiceExtensions
{
public static IServiceCollection AddTrustPilot(this IServiceCollection services)
{
services.AddChangeTokenOptions<TrustPilotOptions>(nameof(TrustPilotOptions), null, configureAction: c => { });
services.AddHttpClient<TrustPilotClient>((sp, client) =>
{
var options = sp.GetRequiredService<IOptionsMonitor<TrustPilotOptions>>();
client.BaseAddress = options.CurrentValue.BaseAddress;
})
.AddHttpMessageHandler(sp =>
{
// get logger
var logger = sp.GetRequiredService<ILogger<AuthorizeHandler<TrustPilotOptions, AuthToken>>>();
var options = sp.GetRequiredService<IOptionsMonitor<TrustPilotOptions>>();
var handlerConfiguration = new AuthorizeHandlerConfiguration<TrustPilotOptions, AuthToken>(
(authClientOptions) =>
{
var body =
$"grant_type=password&username={authClientOptions.Username}&password={authClientOptions.Password}";
var request = new HttpRequestMessage(
HttpMethod.Post,
new Uri(authClientOptions.BaseAddress, "v1/oauth/oauth-business-users-for-applications/accesstoken"))
{
Content = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded")
};
var base64Encoded = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{authClientOptions.ApiKey}:{authClientOptions.ApiSecret}"));
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", base64Encoded);
return request;
},
response =>
{
var accessToken = response.AccessToken;
// https://documentation-apidocumentation.trustpilot.com/faq
// "issued_at" is measured in milliseconds. "expires_in" is measured in seconds. Both are have data type - string.
var createdAt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(response.IssuedAt.GetValueOrDefault());
var expiresIn = createdAt + TimeSpan.FromSeconds(response.ExpiresIn.GetValueOrDefault());
return (accessToken, expiresIn);
});
return new AuthorizeHandler<TrustPilotOptions, AuthToken>(
options.CurrentValue,
handlerConfiguration,
AuthType.Bearer,
logger);
});
return services;
}
}
- Options for this client can be stored inside of Azure Vault.
"TrustPilotOptions": {
"BaseAddress": "https://api.trustpilot.com/",
"ContentType": "application/json",
"ApiKey" : null,
"ApiSecret" : null,
"Username" : null,
"Password" : null
}
using Bet.Extensions.Http.MessageHandlers.Authorize;
namespace Marketing.TrustPilot.Options;
public class TrustPilotOptions : AuthHttpClientOptions
{
public string? ApiKey { get; set; }
public string? ApiSecret { get; set; }
public string? BusinessUnitId { get; set; }
}
- Sample `HttpClient for TrustPilot Api
using System.Net.Http.Json;
using Marketing.TrustPilot.Options;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using i = Marketing.TrustPilot.Models.Invitations;
using r = Marketing.TrustPilot.Models.Reviews;
namespace Marketing.TrustPilot;
public class TrustPilotClient
{
private readonly HttpClient _httpClient;
private readonly ILogger<TrustPilotClient> _logger;
private TrustPilotOptions _options;
public TrustPilotClient(
IOptionsMonitor<TrustPilotOptions> optionsMonitor,
HttpClient httpClient,
ILogger<TrustPilotClient> logger)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_options = optionsMonitor.CurrentValue;
optionsMonitor.OnChange(o => _options = o);
}
public async Task CreateInvitationAsync(i.Invitation invitation, CancellationToken cancellationToken)
{
_httpClient.BaseAddress = new Uri("https://invitations-api.trustpilot.com/v1/private/business-units/");
var response = await _httpClient.PostAsJsonAsync($"{_options.BusinessUnitId}/email-invitations", invitation, SystemTextJson.Options, cancellationToken);
var content = await response.Content.ReadAsStringAsync(cancellationToken);
response.EnsureSuccessStatusCode();
}
public Task<r.Review?> GetReviewAsync(string id, CancellationToken cancellationToken)
{
return _httpClient.GetFromJsonAsync<r.Review>($"v1/private/reviews/{id}", SystemTextJson.Options, cancellationToken);
}
}
- Add Typed Client
ChavahClient
public class ChavahClient : IChavahClient
{
private readonly HttpClient _httpClient;
public ChavahClient(HttpClient httpClient)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
public async Task<IEnumerable<Song>> GetPopular(int count)
{
var response = await _httpClient.GetAsync($"api/songs/getpopular?count={count}");
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<List<Song>>(json);
}
}
- Add Typed Client Configuration in
appsetting.json
"ChavahClient": {
"BaseAddress": "https://messianicradio.com",
"Timeout": "00:05:00",
"ContentType": "application/json"
}
- Add Typed Client Registration in
Startup.cs
services.AddResilienceTypedClient<IChavahClient, ChavahClient>()
.AddDefaultPolicies();