This article is part of a series called Apizr:
- Apizr – Part 1: A Refit based web api client, but resilient
- Apizr – Part 2: Resilient core features
- Apizr – Part 3: More advanced features (this one)
- Apizr – Part 4: Requesting with Mediator pattern
- Apizr – Part 5: Requesting with Optional pattern
After Part 1 with general presentation and Part 2 with core features walk-through, this time we’ll go deeper into some advanced scenarios.
// For both ways options.WithBaseAddress(Constants.MyConfigurationAwareAddress) // Or for static way options.WithBaseAddress(() => GetMyConfigurationAwareAddress()) // Or for extended way: options.WithBaseAddress(serviceProvider => serviceProvider.GetService<MySettingsService>().MyConfigurationAwareAddress)
// For both ways options => options.AddDelegatingHandler(new MyCustomDelegatingHandler()) // Or for static way (with a logHandler instance to log all from your handler) options => options.AddDelegatingHandler(logHandler => new MyCustomDelegatingHandler(logHandler)) // Or for extended way options => options.AddDelegatingHandler(serviceProvider => new MyCustomDelegatingHandler(serviceProvider.GetService<ILogHandler>()))
// For static way options => options.WithHttpClientHandler(new MyCustomHttpClientHandler()) // Or extended way options => options.WithHttpClientHandler(serviceProvider => new MyCustomHttpClientHandler())
This one gives you access to several deep settings.
This is where you can deal with cookies, decompression, proxy, certificate validation and many more…
Something like this dummy example:
options => options.WithHttpClientHandler(serviceProvider => new HttpClientHandler { CookieContainer = serviceProvider.GetRequiredService<ISessionService>().Cookies, #if DEBUG ServerCertificateCustomValidationCallback = (message, certificate, chain, sslPolicyErrors) => true #endif })
The extended way let you adjust more and more HttpClient settings with the HttpClientBuilder:
// For extended way only options => options.ConfigureHttpClientBuilder(httpClientBuilder => httpClientBuilder.WhateverSettings)
Use this one with caution as Apizr makes use of it, so you could override things that could leads to unstable experience.
This is where you can configure complexe policies for example, like:
var timeout = Policy.TimeoutAsync<HttpResponseMessage>( TimeSpan.FromSeconds(10)); var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>( TimeSpan.FromSeconds(30)); options => options.ConfigureHttpClientBuilder(httpCientBuilder => httpCientBuilder.AddPolicyHandler(request => request.Method == HttpMethod.Get ? timeout : longTimeout))
You can provide your own RefitSettings:
// For both ways optiond.WithRefitSettings(new RefitSettings()) // Or for static way options.WithRefitSettings(() => new RefitSettings()) // Or for extended way: options.WithRefitSettings(serviceProvider => new RefitSettings())
RefitSettings let you adjust some Refit parameters like the ContentSerializer, the UrlParameterFormatter or the FormUrlEncodedParameterFormatter.
Something like:
options => options.WithRefitSettings(new RefitSettings(new NewtonsoftJsonContentSerializer(new JsonSerializerSettings { Error = delegate(object sender, ErrorEventArgs args) { Logger.Write(args.ErrorContext.Error)(); args.ErrorContext.Handled = true; }, Converters = {new IsoDateTimeConverter()} })))
Please refer to Refit official documentation to know more about it.
Registry
You may want to register multiple api interfaces within the same project. Also, you may want to share some common configuration between apis without repeating yourself, but at the same time, you may need to set some specific ones for some of it. This is where the ApizrRegistry comes on stage.
Here is an example with extended approach but the same could be done with the static one:
// Some policies var registry = new PolicyRegistry { { "TransientHttpError", HttpPolicyExtensions.HandleTransientHttpError().WaitAndRetryAsync(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10) }) } }; services.AddPolicyRegistry(registry); // Apizr registration services.AddApizrRegistry( registry => registry .AddManagerFor<IReqResService>() .AddManagerFor<IHttpBinService>( options => options .WithLogging( HttpTracerMode.Everything, HttpMessageParts.All, LogLevel.Trace)) .AddCrudManagerFor<User, int, PagedResult<User>, IDictionary<string, object>>( options => options .WithBaseAddress("https://reqres.in/api/users"))), config => config .WithPolicyRegistry(registry) .WithAkavacheCacheHandler() .WithLogging( HttpTracerMode.ExceptionsOnly, HttpMessageParts.ResponseAll, LogLevel.Error) );
And here is what we’re saying in this example:
- Add a manager for IReqResService api interface into the registry, to register it into the container
- Add a manager for IHttpBinService api interface into the registry, to register it into the container
- Apply logging options dedicated to IHttpBinService’s manager
- Add a manager for User entity with CRUD api interface and custom types into the registry, to register it into the container
- Apply address option dedicated to User’s manager
- Apply common options to all managers by:
- Providing a policy registry
- Providing a cache handler
- Providing some logging settings (won’t apply to IHttpBinService’s manager as we set some specific ones)
See? It’s like doing some shopping about what we want for all but this one, and so on…
There’s some other cool things comming with the registry.
With extended approach, everything is auto registered into your DI container, where with static approach, the returned registry instance let you populate everthing so that you could register all by yourself:
// Apizr registry var apizrRegistry = Apizr.CreateRegistry( registry => registry ..., config => config ... ); // Container registration apizrRegistry.Populate((type, factory) => myContainer.RegistrationMethodFactory(type, factory) );
Also, with the registry itself registered (extended: auto, static: manual), you can definitly inject/resolve IApizrRegistry
and get your managed api interfaces from it, where and when you need it:
var reqResManager = apizrRegistry.GetManagerFor<IReqResService>(); // OR var userManager = apizrRegistry.GetCrudManagerFor<User>();
The same for IApizrMediationRegistry
if you’re using MediatR and IApizrOptionalMediationRegistry
if you’re using Optional.Async
A single interface to rule them all.
AddApizrFor<IMyAwesomeApi, MyAwsomeManager<IMyAwesomeApi>>>()