{"id":19711594,"url":"https://github.com/wjsoftware/wj.ocelot.configuration","last_synced_at":"2026-06-09T13:31:06.540Z","repository":{"id":65789216,"uuid":"558663925","full_name":"WJSoftware/wj.Ocelot.Configuration","owner":"WJSoftware","description":"Configuration package that builds Ocelot route configurations for microservices in a way that allows easy per-environment overrides.","archived":false,"fork":false,"pushed_at":"2022-11-02T06:58:11.000Z","size":26,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-27T19:55:31.844Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/WJSoftware.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-10-28T02:35:24.000Z","updated_at":"2024-09-25T11:33:44.000Z","dependencies_parsed_at":"2023-02-10T05:25:11.295Z","dependency_job_id":null,"html_url":"https://github.com/WJSoftware/wj.Ocelot.Configuration","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/WJSoftware/wj.Ocelot.Configuration","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WJSoftware%2Fwj.Ocelot.Configuration","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WJSoftware%2Fwj.Ocelot.Configuration/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WJSoftware%2Fwj.Ocelot.Configuration/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WJSoftware%2Fwj.Ocelot.Configuration/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WJSoftware","download_url":"https://codeload.github.com/WJSoftware/wj.Ocelot.Configuration/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WJSoftware%2Fwj.Ocelot.Configuration/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34110010,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-11T22:12:36.778Z","updated_at":"2026-06-09T13:31:06.517Z","avatar_url":"https://github.com/WJSoftware.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# wj.Ocelot.Configuration\n\u003e Configuration package that builds Ocelot route configurations for microservices in a way that allows easy \n\u003e per-environment overrides.\n\n# Your Feedback Is Important!\n\nThis package is in its early stages.  We would like to hear about the most common configured and overridden properties \nof the Ocelot configuration.  Please take just 2 minutes of your time to visit this [issue in Github](https://github.com/WJSoftware/wj.Ocelot.Configuration/issues/1) \nto tell us about your Ocelot configuration needs.\n\n# Quickstart\n\n1. Install the nuget package.\n2. Create a class that inherits from `GatewayRoutes`.\n3. Add one property per microservice of type `OcelotRouteGroup\u003cOcelotRoute\u003e`.\n4. Open your `appsettings.json` file and add a new section for your Ocelot configuration.\n5. Inside this new section, follow the new arrangement to configure Ocelot (see example below).\n6. Add the Ocelot configuration to the configuration builder.\n\n## Quickstart Details\n\nInstall the package using your preferred method.  For example, using the `dotnet` CLI:\n\n```powershell\ndotnet add package wj.Ocelot.Configuration\n```\n\nNow create a new class that derives from `GatewayRoutes`:\n\n```csharp\nusing RouteGroup =  OcelotRouteGroup\u003cOcelotRoute\u003e;\npublic class OcelotRoutes : GatewayRoutes\u003cRouteGroup, OcelotRoute\u003e\n{\n    #region Microservices\n    public RouteGroup MicroSvcA { get; set; }\n    public RouteGroup MicroSvcB { get; set; }\n    // Etc. One property per microservice.\n    #endregion\n}\n```\n\nNow to the `appsettings.json` file.  Something like this.  This is where the usefulness of this package becomes \nevident.  You only specify the microservice's host name, port, scheme and root path once.  This means that any needed \nper-environment configuration override is easily done and is only done once.  For a detailed explanation see the *[Why \nThis Package Is Needed](#why-this-package-is-needed)* section.\n\n```json\n{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  \"AllowedHosts\": \"*\",\n  \"Ocelot\": { // \u003c-----  This is the section of interest.\n    \"RootPath\": \"/api\", // \u003c--- Usually we assign a root path like this to the gateway microservice in K8s.\n    \"MicroSvcA\": { // \u003c--- The A microservice.\n      \"Host\": \"microsvc-a-svc\", // \u003c--- Usually the K8s service name.\n      \"Port\": 80, // \u003c--- This is the default because in K8s we usually just do HTTP internally.  Added just for clarity.  This can go.\n      \"DownstreamScheme\": \"http\", // \u003c--- HTTP is the default, so no need to specify.  Added just for clarity.  This can go.\n      \"RootPath\": \"/msvcA\", // \u003c--- Root path addition to identify the A microservice routes.  Optional.\n      \"Routes\": [ // \u003c--- Now you only specify per-route stuff.  Host et. al. are inherited from the parent.\n        {\n          \"DownstreamPathTemplate\": \"/resourceX\",\n          \"UpstreamPathTemplate\": \"/resourceX\", // \u003c--- If equal to DownstreamPathTemplate, don't specify.\n          \"UpstreamHttpMethod\": [\n            \"Get\", \"Post\"\n          ]\n        },\n        {\n          \"DownstreamPathTemplate\": \"/resourceX/{id}\",\n          // \"UpstreamPathTemplate\": \"/resourceX/{id}\", \u003c--- Same as DownstreamPathTemplate, so not specified.\n          \"UpstreamHttpMethod\": [\n            \"Get\", \"Put\", \"Patch\", \"Delete\"\n          ]\n        }\n      ]\n    },\n    \"MicroSvcB\": { // \u003c--- This one is the same as MicroSvcA, but taking advantage of the libary features.\n      \"Host\": \"microsvc-b-svc\",\n      \"RootPath\": \"/msvcB\",\n      \"Routes\": [\n        {\n          \"DownstreamPathTemplate\": \"/resourceY\",\n          \"UpstreamHttpMethod\": [\n            \"Get\", \"Post\"\n          ]\n        },\n        {\n          \"DownstreamPathTemplate\": \"/resourceY/{id}\",\n          \"UpstreamHttpMethod\": [\n            \"Get\", \"Put\", \"Patch\", \"Delete\"\n          ]\n        }\n      ]\n    }\n  }\n}\n```\n\nAt this point one would go to `appsettings.Development.json` and any other number of appsettings files to do \nper-environment overrides.  If you want to see an example, read the next section.\n\nFinally, the above configuration needs to be translated to the rigid Ocelot configuration format.  Thanks to this \npackage, though, this is a breeze and done in 2 lines of code.\n\nFor **.Net6** with the simplified `program.cs` file:\n\n```csharp\n// Get the configuration we wrote in the appsettings.json files.\nvar ocelotConfig = builder.Configuration.GetSection(\"Ocelot\").Get\u003cOcelotRoutes\u003e();\n// Pass it along to the extension method.\nbuilder.Configuration.AddOcelotConfiguration(ocelotConfig);\n```\n\nFor **.Net6** projects that don't use the simplified `program.cs` file (such as projects that were migrated to .Net6 \nfrom, say, .Net5):\n\n```csharp\nvar builder = hostBuilder\n    .ConfigureWebHostDefaults(webBuilder =\u003e\n    {\n        webBuilder.UseStartup\u003cStartup\u003e();\n        webBuilder.ConfigureAppConfiguration(configBuilder =\u003e\n        {\n            var configuration = configBuilder.Build();\n            var ocelotConfig = configuration.GetSection(\"Ocelot\").Get\u003cOcelotRoutes\u003e();\n            configBuilder.AddOcelotConfiguration(ocelotConfig);\n        });\n    });\n```\n\nAnd this is it.  Now Ocelot is fully configured.\n\n**IMPORTANT**:  The quickstart example merely shows minimal configuration.  The package actually allows to set \ntimeouts and more in the microservice route group, so that the values apply to all of the microservice routes.  See \nbelow to learn about the ready-to-go Ocelot properties that can be applied to the microservice route group and how to \ncreate a new class that can provide even more Ocelot route properties at this level.\n\n# Why This Package Is Needed\n\nOcelot is a practical way to route HTTP calls in a microservice architecture.  However, the configuration is not \nfriendly at all when it comes to defining the routes in terms of **DRYness** (Don't Repeat Yourself) and value \noverrides.\n\nSo what is the problem with Ocelot's configuration design?  Simple:  The `Routes` property in the configuration JSON \nis an **array**.  The .Net Configuration engine cannot override properties at the *array element* level.  This means \nthat even if only one tiny piece of information needs to change in a 1000-line route configuration in a different \nenvironment, all 1000 lines must be repeated in a new configuration file.\n\nThe second problem is that some required values must always be repeated, namely the microservice's host, port number, \nscheme, and there's no single property to set a root path.  This means that common pieces of routes will be repeated \nin the `UpstreamPathTemplate` property in every route.\n\nSo **wj.Ocelot.Configuration** was born to re-design the route configuration in a manner that allows easy, \nsingle-place configuration value declarations that can be as easily overridden by following the rules of the .Net \nConfiguration engine.\n\nAs an example, let's create the `appsettings.Development.json` file for the [Quickstart](#quickstart-details) example.\n\nUsually, the `Development` environment is the environment used by developers when running a project in their own local \nmachines.  This means most likely that a developer that runs the gateway project will also be running some other \nprojects, like the *MicroSvcA* project described in the configuration example in **Quickstart**.  This means, that in \n`Development`, the server and port must be reconfigured to `localhost` and whatever port the *MicroSvcA* project is \nbeing run.\n\nWithout this package, the developer would be forced to copy the entire routes configuration to just make the host and \nport changes.  Furthermore, the host and port specification is repeated in every route definition for the same \nmicroservice, so configuration data must be repeated, disrespecting the **DRY**.  With this package, the route \ninformation is not duplicated and can actually be targetted for override by the .Net Configuration engine.\n\nThis would be the `appsettings.Development.json` file for the **Quickstart** example:\n\n```json\n{\n  \"Ocelot\": {\n    \"MicroSvcA\": {\n      \"Host\": \"localhost\",\n      \"Port\": 7007,\n    },\n    \"MicroSvcB\": {\n      \"Host\": \"localhost\",\n      \"Port\": 7008,\n    }\n  }\n}\n```\n\nThat tiny environment-specific JSON file will not grow.  The microservices could be defining thousands of different \nroutes, and this file would not change one bit.\n\n# What Comes in the Box\n\n**wj.Ocelot.Configuration** comes with 3 basic classes and one extension method (2 overloads).  Each class is used to \nread the new configuration hierarchy at the different levels (individual route, microservice and gateway) and all can \nbe used as base classes to increase the number of supported properties.\n\n## The OcelotRoute Class\n\nThis is the class used at the inner-most level in the configuration hierarchy:  The individual route level.\n\nThis class will, over time and following demand, acquire new properties that mirror the properties in the **Ocelot** \nroute configuration.  To enter the technical realm, this class mimics the most popular properties in the `FileRoute` \nclass provided by the **Ocelot** package.  This class was born because many of the `FileRoute` properties are not \nnullable, and nullable is something required when doing parent inheritance of property values.\n\nFor example, the `FileRoute` class has the `Priority` property, which is of type `int` (as opposed to `int?` or \n`Nullable\u003cint\u003e`).  Because, if not specified, it will have the value `0`, one cannot know if this `0` is because it \nwasn't specified in configuration or if it was specified in configuration as `0` at the individual route level and \ntherefore it should not be overridden by the parent's value.\n\nFrom the above paragraph, one can infer that properties added to this class in the future, or properties added to a \nderived class, should be of nullable return types.  This way, if a value is not specified, the property value will be \n`null`, and it is therefore easy to spot as an opportunity to inherit the value from the route's parent (of type \n`OCelotRouteGroup` or a derived class).\n\nThe currently defined properties of this class are:\n\n| Property | Maps to (in `FileRoute`) | Package Version | Remarks |\n| - | - | - | - |\n| `DownstreamPathTemplate` | `DownstreamPathTemplate` | 0.1.0\n| `UpstreamPathTemplate` | `UpstreamPathTemplate` | 0.1.0 | Not a 1:1 relation.  The upstream template inherits the values of `RootPath` from the parents.  Also, if left unspecified in the JSON file (it will be `null`), will acquire the value of `DownstreamPathTemplate`. |\n| `UpstreamHttpMethod` | `UpstreamHttpMethod` | 0.1.0\n| `TimeOut` | `QoSOptions.TimeoutValue` | 0.1.0 | This one is of type `TimeSpan?` (as opposed to `int` in `FileRoute`). |\n| `Priority` | `Priority` | 0.1.0 | The only difference is that this class' property is nullable:  `int?`.\n\nIf you are in need of an Ocelot configuration property not found here, simply inherit from `OcelotRoute` and add the \nneeded configuration properties.  Note that adding it here will only provide individual-route visiblity.  Apply this \nsame reasoning with the `OcelotRouteGroup` class (explained next) if you want the properties to apply to all routes in \na microservice.\n\nWhenever possible, name the property the same as in Ocelot's `FileRoute` class so the standard mapping algorithm can \nautomatically pick it up for mapping.  For this to work, however, the property's data type must be the same (in \nnullable version).  More about this topic [later](#the-property-value-mapping-algorithm).\n\n## The OcelotRouteGroup Class\n\nThis is the class used at the middle level in the configuration hierarchy:  The microservice level.\n\n\u003e **ABOUT THE TERM \"MICROSERVICE\"**: As you probably have noted, the term *microservice* is used a lot here.  Ocelot, \n\u003e however, does not necessarily only work in a microservices scenario.  While this document uses the microservices \n\u003e example because it is easier to understand, strictly speaking a single microservice could have more than one group \n\u003e in the configuration JSON file.  This is why this class is called a **route group**¨.  Routes are logically grouped \n\u003e together to ease configuration.  Per-microservice grouping is not really a requirement.\n\nMost often than not, the same configuration value is repeated endlessly across routes that belong to a specific \nmicroservice.  This is not **DRY**, so this package provides the means to eliminate this:  Per-microservice settings.\n\nThis class' properties, excluding the `Routes` property, also mimic Ocelot's `FileRoute` class, except of course, \nmaking sure nullable data types are used.  The values present here will apply to all individual routes in the `Routes` \ncollection, as long as the individual routes do not specify a value of their own.\n\nInherit from this class and add properties to expand the number of microservice-level properties available to your \nproject.\n\nThe currently defined properties of this class are:\n\n| Property | Maps to (in `FileRoute`) | Package Version | Remarks |\n| - | - | - | - |\n| `Host` | `DownstreamHostAndPort[].Host` | 0.1.0 | Set in tandem with the `Port` property. |\n| `Port` | `DownstreamHostAndPort[].Port` | 0.1.0 | Set in tandem with the `Host` property. |\n| `DownstreamScheme` | `DownstreamScheme` | 0.1.0\n| `TimeOut` | `QoSOptions.TimeoutValue` | 0.1.0 | This one is of type `TimeSpan?` (as opposed to `int` in `FileRoute`). |\n| `Priority` | `Priority` | 0.1.0 | The only difference is that this class' property is nullable:  `int?`.\n| `RootPath` | As a part of `UpstreamPathTemplate` | 0.1.0 | See [this section](#the-in-box-mapping-algorithm-explained) for details. |\n\n## The GatewayRoutes Class\n\nThis is the class used at the top level in the configuration hierarchy:  The gateway level.\n\nThis is the only class of the three that is abstract, meaning it can only be used as a base class.  This is to clearly \nindicate that a derived class is required because the library cannot possibly know how many or even if there is a \nminimum number of route groups (microservices, to continue with the term).  In short, the library needs the route \ngroups to be defined.\n\nInherit from this class as shown in the [Quickstart details](#quickstart-details), creating one property for each \nmicroservice (route group).  These properties will be discovered using reflection, so it is important that they follow \nthe instructions regarding the property's data type.\n\nThe currently defined properties of this class are:\n\n| Property | Maps to (in `FileRoute`) | Package Version | Remarks |\n| - | - | - | - |\n| `RootPath` | As a part of `UpstreamPathTemplate` | 0.1.0 | See [this section](#the-in-box-mapping-algorithm-explained) for details. |\n\n## The Extensions Class\n\nThis is a static class that defines the `AddOcelotConfiguration()` extension method for the `IConfigurationBuilder` \ninterface.\n\nThe method comes with 2 overloads.  The simpler one is for the cases where the properties already mapped by the \nlibrary are enough for the gateway application.  In this case, only one custom type will have been made (the gateway \nroutes class).  This overload does not allow setting a custom mapper function and will work solely using the in-box \nmapping algorithm.\n\nThe second overload, on the other hand, will require the specification of all the types used in configuration (for the \n3 levels gateway, microservice and individual route), and will allow the inclusion of a new mapping algorithm.  This \nis not a requirement, though, because the in-box algorithm can pick up new properties as long as they are named the \nsame as in Ocelot's `FileRoute` class, and have the same return type (or its nullable version).\n\n**IMPORTANT**:  It his highly recommended to always use nullable types.\n\n## The Property Value Mapping Algorithm\n\nThe in-box property mapping algorithm is based on 2 qualities of the properties:  **Name** and **return type**.  If a \nproperty is found in `OcelotRoute` or `OcelotRouteGroup` that has the same name and return type as a property in \nOcelot's `FileRoute` class, it will transfer the value.  As already mentioned, the return type will be considered a \nmatch if it is either an exact match, or the nullable version of Ocelot's return type.\n\nOnce the match has been established, the value is simply calculated by giving the individual route level priority over \nthe route group (microservice) level.  In other words, if the individual route specify a value, that value will be \nset; if no individual route value is present, then the route group value is the one set.  If both values are absent, \nthen the property will be left with Ocelot's default value.\n\n### The In-Box Mapping Algorithm Explained\n\nIntentionally missing the automatic property match can be helpful.  For example, this library intentionally misses the \nautomatic match for the `TimeoutValue` property in several ways.  First of all, it is not part of a sub-object, while \nin Ocelot it is a property in a sub-object.  With this alone the automatic match is guaranteed to fail.  This, \nhowever, is not the only change.  The matching property is not named the same.  This library has named the property \n`TimeOut`, and its data type is `TimeSpan?`, not `int`.  This is an opinionated decision to more easily specify the \ntimeout value.\n\nAnother good reason to avoid embedded properties is because the current algorithm does not recursively traverse \nproperty values to individually set values.  This is an unsupported scenario.  The current algorithm is only capable \nof setting the vlaue of a property with a custom object as a whole.  It will not drill down that object to \nindividually set properties.\n\nAs a final example, let's talk about the `UpstreamPathTemplate` property.  It is defined with the same name as in the \nOcelot library, and it has the same data type.  So why is it avoiding the automatic matching?   For 2 reasons:\n\n1. It is built from pieces.  The actual value configured in Ocelot will be the chaining of the gateway's root path, \nthe group level's root path, and finally its own value.\n2. It will acquire the value from `DownstreamPathTemplate` if left unspecified.  Most often than not and thanks to the \nREST specification, these two will match.  Implementing this copy operation supports the **DRY** principle.\n\n### Providing a New Mapping Function\n\nThis is something that is only needed if properties are added at the individual route or group route levels that do \nnot match any of Ocelot's `FileRoute` properties as explained previously, or if the value will undergo some extra \nlogic like in the examples in the previous section.\n\nThe mapping logic is provided when configuring using the `IConfiguraitonBuilder`'s extension method \n`AddOcelotConfiguration()`.  Generally speaking, it is done like this:\n\n```csharp\nbuilder.Configuration.AddOcelotConfiguration\u003cMyRoutes, MyRouteGroup, MyRoute\u003e(ocelotConfig, opt =\u003e\n{\n    opt.MapperDelegate = (route, parent, rootPath) =\u003e\n    {\n        FileRoute fr = opt.DefaultMapperDelegate(route, parent, rootPath);\n        // Now do whatever you want with the FileRoute object.\n        // The route and parent parameters are the individual route and route group level configurations.\n        // The rootPath parameter is the gateway's root path.\n    };\n});\n```\n\nAs shown in the example, it is highly recommended to always call the default mapper in order to only have to provide \nthe additional logic required for the additional properties defined in `MyRoute` or `MyRouteGroup` that aren't matched \nby the in-box algorithm.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwjsoftware%2Fwj.ocelot.configuration","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwjsoftware%2Fwj.ocelot.configuration","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwjsoftware%2Fwj.ocelot.configuration/lists"}