Apizr – Part 3: More advanced features

apizr3

This article is part of a series called Apizr:

Read - Documentation  Browse - Source

After Part 1 with general presentation and Part 2 with core features walk-through, this time we’ll go deeper into some advanced scenarios.

ADVANCED CORE FEATURES
The options builder provide you some advanced core features.
BASE ADDRESS
The WebApi attribute let you define the base address with a simple attribute decoration over your api interface.
But sometime, we need to change the endpoint, targeting dev, test or production server uri.
To deal with it, the builder expose a method called WithBaseAddress to let you set it by code, like:
// 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)
DELEGATING HANDLER
You can register your own DelegatingHandler , it will be integrated with all others by Apizr:
// 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>()))
This is what Apizr use to manage authentication, prioritization, policies and network traces.
If there’re some custom scenarios you want to cover while sending the request, just provide your own DelegatingHandler.
HTTP CLIENT HANDLER
You can provide your own HttpClientHandler  to Apizr:
// 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
        })
HTTP CLIENT BUILDER

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))
REFIT SETTINGS

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.

>> Doc article <<

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.

>> Doc article <<

OVERRIDING
Almost everything could be overridden with Apizr.
You found everything you need and ready to play? Pleased to hear that!
You’d like to adjust some behaviors while requesting?
Well you can provide your own handlers as we’ve seen it (http, logging, caching, networking).
But guess what? You also can provide your own implementation of the heart of the beast aka IApzrManager<TWebApi>.
When you think you’re done designing your own ApizrManager, just provide it while initializing Apizr, like:
AddApizrFor<IMyAwesomeApi, MyAwsomeManager<IMyAwesomeApi>>>()
CONCLUSION
In this short article we talked about advanced Apizr core features. But we still can get extended features thanks to some integrations NuGet packages.
In the next one, I’ll talk about requesting with Mediator pattern to keep all things loosely coupled, simple and unified.
You’ll find all sources and samples on GitHub.

Specialized since 2013 in cross-platform applications development for iOS, Android and Windows, using technologies such as Microsoft Xamarin and Microsoft Azure. Initially focused, since 2005, on development, then administration of Customer Relationship Management systems, mainly around solutions such as Microsoft SharePoint and Microsoft Dynamics CRM.

Related Posts

Leave a comment

13 + 8 =