Dependency Injection

Namespace: Ocelot.DependencyInjection
Source code: DependencyInjection

Services Overview

Dependency Injection feature in Ocelot is designed to extend and/or control the building of Ocelot Core as ASP.NET Core pipeline services. The main methods of the ServiceCollectionExtensions class are:

Use IServiceCollection extensions in your Program (ASP.NET Core app) to add and build Ocelot Core services. The fact is, the OcelotBuilder class is Ocelot’s cornerstone logic.

IServiceCollection extensions

Class: Ocelot.DependencyInjection. ServiceCollectionExtensions

Based on the current implementations for the OcelotBuilder class, the AddOcelot method adds the required ASP.NET services to the DI container. You could call the more extended AddOcelotUsingBuilder method while configuring services to build and use a custom builder via an IMvcCoreBuilder object.

AddOcelot method

Signatures:

IOcelotBuilder AddOcelot(this IServiceCollection services);
IOcelotBuilder AddOcelot(this IServiceCollection services, IConfiguration configuration);

These IServiceCollection extension methods add default ASP.NET services and Ocelot application services with configuration injected implicitly or explicitly.

Note: Both methods add the required and default ASP.NET Core services for Ocelot Core in the AddDefaultAspNetServices method, which is the default builder.

In this scenario, you do nothing other than call the AddOcelot method, which is often mentioned in feature chapters if additional startup settings are required. With this method, you simply reuse the default settings to build the Ocelot Core. The alternative is the AddOcelotUsingBuilder method; see the next subsection.

AddOcelotUsingBuilder method

Signatures:

using CustomBuilderFunc = System.Func<IMvcCoreBuilder, Assembly, IMvcCoreBuilder>;

IOcelotBuilder AddOcelotUsingBuilder(this IServiceCollection services, CustomBuilderFunc customBuilder);
IOcelotBuilder AddOcelotUsingBuilder(this IServiceCollection services, IConfiguration configuration, CustomBuilderFunc customBuilder);

These IServiceCollection extension methods add Ocelot application services and custom ASP.NET Core services with configuration injected implicitly or explicitly.

Note: The method adds custom ASP.NET Core services required for Ocelot Core using a custom builder (aka customBuilder parameter). It is highly recommended to read the documentation of the AddDefaultAspNetServices method, or even review the implementation to understand the default ASP.NET Core services which are the minimal part of the gateway pipeline.

In this custom scenario, you control everything during the ASP.NET Core build process, and you provide custom settings to build Ocelot Core.

OcelotBuilder class

The OcelotBuilder class is the core of Ocelot which does the following:

  • Contructs itself by single public constructor:

    public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot, Func<IMvcCoreBuilder, Assembly, IMvcCoreBuilder> customBuilder = null);
    
  • Initializes and stores public properties: Services (of IServiceCollection type), Configuration (of IConfiguration type), and MvcCoreBuilder (of IMvcCoreBuilder type).

  • Adds all application services during the construction phase via the Services property.

  • Adds ASP.NET Core services by builder using Func<IMvcCoreBuilder, Assembly, IMvcCoreBuilder> object in these 2 development scenarios:

  • Adds ASP.NET Core services by builder using a Func<IMvcCoreBuilder, Assembly, IMvcCoreBuilder> object in these two development scenarios:

    1. By default builder (AddDefaultAspNetServices method) if there is no customBuilder parameter provided.

    2. By Custom Builder with the provided delegate object as the customBuilder parameter.

  • Adds (switches on/off) Ocelot features through the following methods:

    • AddSingletonDefinedAggregator and AddTransientDefinedAggregator methods

    • AddCustomLoadBalancer method

    • AddDelegatingHandler method

    • AddConfigPlaceholders method

AddDefaultAspNetServices method

Part of the OcelotBuilder class

Currently, the method is protected, and overriding is forbidden. The role of the method is to inject the required services via both the IServiceCollection and IMvcCoreBuilder interface objects for the minimal part of the gateway pipeline.

Current implementation is the folowing:

protected IMvcCoreBuilder AddDefaultAspNetServices(IMvcCoreBuilder builder, Assembly assembly)
{
    Services
        .AddLogging()
        .AddMiddlewareAnalysis()
        .AddWebEncoders();
    return builder
        .AddApplicationPart(assembly)
        .AddControllersAsServices()
        .AddAuthorization()
        .AddNewtonsoftJson();
}

The method cannot be overridden. It is not virtual, and there is no way to override the current behavior by inheritance. The method is the default builder of Ocelot Core when calling the AddOcelot method. As an alternative, to “override” this default builder, you can design and reuse a custom builder as a Func<IMvcCoreBuilder, Assembly, IMvcCoreBuilder> delegate object and pass it as a parameter to the AddOcelotUsingBuilder method. It gives you full control over the design and building of Ocelot Core, but be careful when designing your custom Ocelot pipeline as a customizable ASP.NET Core pipeline.

Warning: Most of the services from the minimal part of the pipeline should be reused, but only a few services can be removed.

Warning: The method above is called after adding the required services of the ASP.NET Core pipeline by the AddMvcCore method via the Services property in the upper calling context. These services are the absolute minimum core services for the ASP.NET MVC pipeline. They must always be added to the DI container and are added implicitly before calling the method by the caller in the upper context. So, AddMvcCore creates an IMvcCoreBuilder object and assigns it to the MvcCoreBuilder property. Finally, as a default builder, the method above receives the IMvcCoreBuilder object, making it ready for further extensions.

The next section shows you an example of designing a custom Ocelot Core using a custom builder.

Custom Builder

Goal: Replace Newtonsoft.Json services with System.Text.Json services.

Problem

The main AddOcelot method adds Newtonsoft JSON services using the AddNewtonsoftJson extension method in the default builder (AddDefaultAspNetServices method). The AddNewtonsoftJson method was introduced in earlier .NET and Ocelot releases, which was necessary before Microsoft launched the System.Text.Json library. However, it now affects normal use, so we intend to solve the problem.

Modern JSON services out of the box will help configure JSON settings using the JsonSerializerOptions property for JSON formatters during (de)serialization.

Solution

We have the following methods in ServiceCollectionExtensions class:

IOcelotBuilder AddOcelotUsingBuilder(this IServiceCollection services, Func<IMvcCoreBuilder, Assembly, IMvcCoreBuilder> customBuilder);
IOcelotBuilder AddOcelotUsingBuilder(this IServiceCollection services, IConfiguration configuration, Func<IMvcCoreBuilder, Assembly, IMvcCoreBuilder> customBuilder);

These methods with a custom builder allow you to use any desired JSON library for (de)serialization. However, we are going to create a custom MvcCoreBuilder with support for JSON services, such as System.Text.Json. To do that, we need to call the AddJsonOptions extension of the MvcCoreMvcCoreBuilderExtensions class (NuGet Microsoft.AspNetCore.Mvc.Core package) in Program:

  builder.Services
      .AddLogging()
      .AddMiddlewareAnalysis()
      .AddWebEncoders()
      // Add your custom builder
      .AddOcelotUsingBuilder(builder.Configuration, MyCustomBuilder);

  static IMvcCoreBuilder MyCustomBuilder(IMvcCoreBuilder builder, Assembly assembly) => builder
      .AddApplicationPart(assembly)
      .AddControllersAsServices()
      .AddAuthorization()
      // Replace AddNewtonsoftJson() by AddJsonOptions()
      .AddJsonOptions(options =>
      {
          options.JsonSerializerOptions.WriteIndented = true; // use System.Text.Json
      });

The sample code provides settings to render JSON as indented text rather than as compressed plain JSON text without spaces. This is just one common use case, and you can add additional services to the builder.


Configuration Overview

Dependency Injection for the Configuration feature in Ocelot is designed to extend and set up the configuration of the Ocelot Core before the stage of building ASP.NET Core services (see Services Overview). To configure the Ocelot Core services, use the IConfigurationBuilder extensions in your Program of your gateway app.

IConfigurationBuilder extensions

Class: Ocelot.DependencyInjection. ConfigurationBuilderExtensions

The main methods are the AddOcelot methods within the ConfigurationBuilderExtensions class. These methods have a list of overloaded versions with corresponding signatures.

The purpose of the AddOcelot method is to prepare everything before actually configuring with native extensions. It involves the following steps:

  1. Merging Partial JSON Files: The GetMergedOcelotJson method merges partial JSON files.

  2. Selecting Merge Type: It allows you to choose a merge type to save the merged JSON configuration data either ToFile or ToMemory.

  3. Framework Extensions: Finally, the method calls the following native IConfigurationBuilder framework extensions:

  • The AddJsonFile method adds the primary configuration file (commonly known as ocelot.json) after the merge stage. It writes the file back to the file system using the ToFile merge type option, which is implicitly the default.

  • The AddJsonStream method adds the JSON data of the primary configuration file as a UTF-8 stream into memory after the merge stage. It uses the ToMemory merge type option.

AddOcelot methods

Signatures of the most common versions:

IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, IWebHostEnvironment env);
IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, string folder, IWebHostEnvironment env);

Note: These versions use the implicit ToFile merge type to write ocelot.json back to disk. Finally, they call the AddJsonFile extension.

Signatures of the versions to specify a MergeOcelotJson option:

IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, IWebHostEnvironment env, MergeOcelotJson mergeTo,
    string primaryConfigFile = null, string globalConfigFile = null, string environmentConfigFile = null, bool? optional = null, bool? reloadOnChange = null);
IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, string folder, IWebHostEnvironment env, MergeOcelotJson mergeTo,
    string primaryConfigFile = null, string globalConfigFile = null, string environmentConfigFile = null, bool? optional = null, bool? reloadOnChange = null);

Note: These versions include optional arguments to specify the location of the three main files involved in the merge operation. In theory, these files can be located anywhere, but in practice, it is better to keep them in one folder.

Signatures of the versions to indicate the FileConfiguration object of a self-created out-of-the-box configuration: [1]

IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, FileConfiguration fileConfiguration,
    string primaryConfigFile = null, bool? optional = null, bool? reloadOnChange = null);
IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, FileConfiguration fileConfiguration, IWebHostEnvironment env, MergeOcelotJson mergeTo,
    string primaryConfigFile = null, string globalConfigFile = null, string environmentConfigFile = null, bool? optional = null, bool? reloadOnChange = null);

Note 1: These versions include optional arguments to specify the location of the three main files involved in the merge operation.

Note 2: Your FileConfiguration object can be serialized/deserialized from anywhere: local or remote storage, Consul KV storage, and even a database. For more information about this super useful feature, please read PR 1569.