From 07e4d29461803fc5568015c079e9239c3c10c44c Mon Sep 17 00:00:00 2001 From: Zhiyuan Liang <141655842+zhiyuanliang-ms@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:55:09 +0800 Subject: [PATCH] Adjust title level in README (#329) * add newline & adjust title level * resolve comments * update * fix typo * add comma * fix typo * version bump to 3.1.1 --- README.md | 102 +++++++++--------- ...rosoft.FeatureManagement.AspNetCore.csproj | 2 +- .../Microsoft.FeatureManagement.csproj | 2 +- 3 files changed, 56 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index b50411b0..d9c25a6b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Microsoft.FeatureManagement](https://img.shields.io/nuget/v/Microsoft.FeatureManagement?label=Microsoft.FeatureManagement)](https://www.nuget.org/packages/Microsoft.FeatureManagement) [![Microsoft.FeatureManagement.AspNetCore](https://img.shields.io/nuget/v/Microsoft.FeatureManagement.AspNetCore?label=Microsoft.FeatureManagement.AspNetCore)](https://www.nuget.org/packages/Microsoft.FeatureManagement.AspNetCore) -Feature flags provide a way for .NET and ASP.NET Core applications to turn features on or off dynamically. Developers can use feature flags in simple use cases like conditional statements to more advanced scenarios like conditionally adding routes or MVC filters. Feature flags build on top of the .NET Core configuration system. Any .NET Core configuration provider is capable of acting as the back-bone for feature flags. +Feature flags provide a way for .NET and ASP.NET Core applications to turn features on or off dynamically. Developers can use feature flags in simple use cases like conditional statements to more advanced scenarios like conditionally adding routes or MVC filters. Feature flags are built on top of the .NET Core configuration system. Any .NET Core configuration provider is capable of acting as the backbone for feature flags. Here are some of the benefits of using this library: @@ -28,8 +28,11 @@ Here are some of the benefits of using this library: * [Feature Flags](#feature-flags) * [Feature Filters](#feature-filters) * [Feature Flag Declaration](#feature-flag-declaration) - * [ASP.NET Core Integration](#ASPNET-Core-Integration) - * [Built-in Feature Filters](#built-in-Feature-Filters) +* [Consumption](#consumption) +* [ASP.NET Core Integration](#ASPNET-core-integration) +* [Implement a Feature Filter](#implementing-a-feature-filter) +* [Providing a Context For Feature Evaluation](#providing-a-context-for-feature-evaluation) +* [Built-in Feature Filters](#built-in-feature-filters) * [Targeting](#targeting) * [Targeting Exclusion](#targeting-exclusion) * [Caching](#caching) @@ -39,7 +42,7 @@ Here are some of the benefits of using this library: Feature flags are composed of two parts, a name and a list of feature-filters that are used to turn the feature on. ### Feature Filters -Feature filters define a scenario for when a feature should be enabled. When a feature is evaluated for whether it is on or off, its list of feature-filters are traversed until one of the filters decides the feature should be enabled. At this point the feature is considered enabled and traversal through the feature filters stops. If no feature filter indicates that the feature should be enabled, then it will be considered disabled. +Feature filters define a scenario for when a feature should be enabled. When a feature is evaluated for whether it is on or off, its list of feature filters is traversed until one of the filters decides the feature should be enabled. At this point the feature is considered enabled and traversal through the feature filters stops. If no feature filter indicates that the feature should be enabled, then it will be considered disabled. As an example, a Microsoft Edge browser feature filter could be designed. This feature filter would activate any features it is attached to as long as an HTTP request is coming from Microsoft Edge. @@ -86,11 +89,11 @@ The feature management library supports appsettings.json as a feature flag sourc } ``` -The `FeatureManagement` section of the json document is used by convention to load feature flag settings. In the section above, we see that we have provided three different features. Features define their feature filters using the `EnabledFor` property. In the feature filters for `FeatureT` we see `AlwaysOn`. This feature filter is built-in and if specified will always enable the feature. The `AlwaysOn` feature filter does not require any configuration so it only has the `Name` property. `FeatureU` has no filters in its `EnabledFor` property and thus will never be enabled. Any functionality that relies on this feature being enabled will not be accessible as long as the feature filters remain empty. However, as soon as a feature filter is added that enables the feature it can begin working. `FeatureV` specifies a feature filter named `TimeWindow`. This is an example of a configurable feature filter. We can see in the example that the filter has a `Parameters` property. This is used to configure the filter. In this case, the start and end times for the feature to be active are configured. +The `FeatureManagement` section of the json document is used by convention to load feature flag settings. In the section above, we see that we have provided three different features. Features define their feature filters using the `EnabledFor` property. In the feature filters for `FeatureT` we see `AlwaysOn`. This feature filter is built-in and if specified will always enable the feature. The `AlwaysOn` feature filter does not require any configuration, so it only has the `Name` property. `FeatureU` has no filters in its `EnabledFor` property and thus will never be enabled. Any functionality that relies on this feature being enabled will not be accessible as long as the feature filters remain empty. However, as soon as a feature filter is added that enables the feature it can begin working. `FeatureV` specifies a feature filter named `TimeWindow`. This is an example of a configurable feature filter. We can see in the example that the filter has a `Parameters` property. This is used to configure the filter. In this case, the start and end times for the feature to be active are configured. **Advanced:** The usage of colon ':' in feature flag names is forbidden. -### On/Off Declaration +#### On/Off Declaration The following snippet demonstrates an alternative way to define a feature that can be used for on/off features. ``` JavaScript @@ -109,7 +112,7 @@ The following snippet demonstrates an alternative way to define a feature that c } ``` -### RequirementType +#### RequirementType The `RequirementType` property of a feature flag is used to determine if the filters should use `Any` or `All` logic when evaluating the state of a feature. If `RequirementType` is not specified, the default value is `Any`. @@ -139,11 +142,25 @@ A `RequirementType` of `All` changes the traversal. First, if there are no filte } ``` -In the above example, `FeatureW` specifies a `RequirementType` of `All`, meaning all of it's filters must evaluate to true for the feature to be enabled. In this case, the feature will be enabled for 50% of users during the specified time window. - +In the above example, `FeatureW` specifies a `RequirementType` of `All`, meaning all of its filters must evaluate to true for the feature to be enabled. In this case, the feature will be enabled for 50% of users during the specified time window. + +## Consumption + +The basic form of feature management is checking if a feature flag is enabled and then performing actions based on the result. This is done through the `IFeatureManager`'s `IsEnabledAsync` method. + +``` C# +… +IFeatureManager featureManager; +… +if (await featureManager.IsEnabledAsync("FeatureX")) +{ + // Do something +} +``` + ### Service Registration -Feature flags rely on .NET Core dependency injection. We can register the feature management services using standard conventions. +Feature management relies on .NET Core dependency injection. We can register the feature management services using standard conventions. ``` C# using Microsoft.FeatureManagement; @@ -165,30 +182,6 @@ You can also specify that feature flag configuration should be retrieved from a services.AddFeatureManagement(configuration.GetSection("MyFeatureFlags")); ``` -### Scoped Feature Management Services - -The `AddFeatureManagement` method adds feature management services as singletons within the application, but there are scenarios where it may be necessary for feature management services to be added as scoped services instead. For example, users may want to use feature filters which consume scoped services for context information. In this case, the `AddScopedFeatureManagement` method should be used instead. This will ensure that feature management services, including feature filters, are added as scoped services. - -``` C# -services.AddScopedFeatureManagement(); -``` - -## Consumption -The simplest use case for feature flags is to do a conditional check for whether a feature is enabled to take different paths in code. The uses cases grow from there as the feature flag API begins to offer extensions into ASP.NET Core. - -### Feature Check -The basic form of feature management is checking if a feature is enabled and then performing actions based on the result. This is done through the `IFeatureManager`'s `IsEnabledAsync` method. - -``` C# -… -IFeatureManager featureManager; -… -if (await featureManager.IsEnabledAsync("FeatureX")) -{ - // Do something -} -``` - ### Dependency Injection When using the feature management library with MVC, the `IFeatureManager` can be obtained through dependency injection. @@ -205,10 +198,20 @@ public class HomeController : Controller } ``` +### Scoped Feature Management Services + +The `AddFeatureManagement` method adds feature management services as singletons within the application, but there are scenarios where it may be necessary for feature management services to be added as scoped services instead. For example, users may want to use feature filters which consume scoped services for context information. In this case, the `AddScopedFeatureManagement` method should be used instead. This will ensure that feature management services, including feature filters, are added as scoped services. + +``` C# +services.AddScopedFeatureManagement(); +``` + ## ASP.NET Core Integration + The feature management library provides functionality in ASP.NET Core and MVC to enable common feature flag scenarios in web applications. These capabilities are available by referencing the [Microsoft.FeatureManagement.AspNetCore](https://www.nuget.org/packages/Microsoft.FeatureManagement.AspNetCore/) NuGet package. ### Controllers and Actions + MVC controller and actions can require that a given feature, or one of any list of features, be enabled in order to execute. This can be done by using a `FeatureGateAttribute`, which can be found in the `Microsoft.FeatureManagement.Mvc` namespace. ``` C# @@ -229,7 +232,7 @@ public IActionResult Index() } ``` -The `Index` MVC action above requires "FeatureX" to be enabled before it can execute. +The `Index` MVC action above requires "FeatureX" to be enabled before it can be executed. ### Disabled Action Handling @@ -296,6 +299,7 @@ services.AddMvc(o => The code above adds an MVC filter named `SomeMvcFilter`. This filter is only triggered within the MVC pipeline if the feature it specifies, "FeatureX", is enabled. ### Razor Pages + MVC Razor pages can require that a given feature, or one of any list of features, be enabled in order to execute. This can be done by using a `FeatureGateAttribute`, which can be found in the `Microsoft.FeatureManagement.Mvc` namespace. ``` C# @@ -348,7 +352,7 @@ Feature filters are registered by calling `AddFeatureFilter` on the `IFeature ### Parameterized Feature Filters -Some feature filters require parameters to decide whether a feature should be turned on or not. For example a browser feature filter may turn on a feature for a certain set of browsers. It may be desired that Edge and Chrome browsers enable a feature, while Firefox does not. To do this a feature filter can be designed to expect parameters. These parameters would be specified in the feature configuration, and in code would be accessible via the `FeatureFilterEvaluationContext` parameter of `IFeatureFilter.EvaluateAsync`. +Some feature filters require parameters to decide whether a feature should be turned on or not. For example, a browser feature filter may turn on a feature for a certain set of browsers. It may be desired that Edge and Chrome browsers enable a feature, while Firefox does not. To do this a feature filter can be designed to expect parameters. These parameters would be specified in the feature configuration, and in code would be accessible via the `FeatureFilterEvaluationContext` parameter of `IFeatureFilter.EvaluateAsync`. ``` C# public class FeatureFilterEvaluationContext @@ -385,7 +389,7 @@ public class BrowserFilter : IFeatureFilter ### Filter Alias Attribute -When a feature filter is registered to be used for a feature flag, the alias used in configuration is the name of the feature filter type with the _Filter_ suffix, if any, removed. For example `MyCriteriaFilter` would be referred to as _MyCriteria_ in configuration. +When a feature filter is registered to be used for a feature flag, the alias used in configuration is the name of the feature filter type with the _Filter_ suffix, if any, removed. For example, `MyCriteriaFilter` would be referred to as _MyCriteria_ in configuration. ``` JavaScript "MyFeature": { @@ -481,11 +485,12 @@ We can see that the `AccountIdFilter` requires an object that implements `IAccou **Note:** Only a single feature filter interface can be implemented by a single type. Trying to add a feature filter that implements more than a single feature filter interface will result in an `ArgumentException`. ### Using Contextual and Non-contextual Filters With the Same Alias + Filters of `IFeatureFilter` and `IContextualFeatureFilter` can share the same alias. Specifically, you can have one filter alias shared by 0 or 1 `IFeatureFilter` and 0 or N `IContextualFeatureFilter`, so long as there is at most one applicable filter for `ContextType`. The following passage describes the process of selecting a filter when contextual and non-contextual filters of the same name are registered in an application. -Let's say you have a non-contextual filter called `FilterA` and two contextual filters `FilterB` and FilterC which accept `TypeB` and `TypeC` contexts respectively. All of three filters share the same alias `SharedFilterName`. +Let's say you have a non-contextual filter called `FilterA` and two contextual filters `FilterB` and FilterC which accept `TypeB` and `TypeC` contexts respectively. All three filters share the same alias `SharedFilterName`. You also have a feature flag `MyFeature` which uses the feature filter `SharedFilterName` in its configuration. @@ -494,13 +499,13 @@ If all of three filters are registered: * When you call IsEnabledAsync("MyFeature", context), if context's type is `TypeB`, `FilterB` will be used. If context's type is `TypeC`, `FilterC` will be used. * When you call IsEnabledAsync("MyFeature", context), if context's type is `TypeF`, `FilterA` will be used. -### Built-In Feature Filters +## Built-In Feature Filters There a few feature filters that come with the `Microsoft.FeatureManagement` package: `PercentageFilter`, `TimeWindowFilter`, `ContextualTargetingFilter` and `TargetingFilter`. All filters, except for the `TargetingFilter`, are added automatically when feature management is registered. The `TargetingFilter` is added with the `WithTargeting` method that is detailed in the `Targeting` section below. Each of the built-in feature filters have their own parameters. Here is the list of feature filters along with examples. -#### Microsoft.Percentage +### Microsoft.Percentage This filter provides the capability to enable a feature based on a set percentage. @@ -517,9 +522,9 @@ This filter provides the capability to enable a feature based on a set percentag } ``` -#### Microsoft.TimeWindow +### Microsoft.TimeWindow -This filter provides the capability to enable a feature based on a time window. If only `End` is specified, the feature will be considered on until that time. If only start is specified, the feature will be considered on at all points after that time. +This filter provides the capability to enable a feature based on a time window. If only `End` is specified, the feature will be considered on until that time. If only `Start` is specified, the feature will be considered on at all points after that time. ``` JavaScript "EnhancedPipeline": { @@ -535,7 +540,7 @@ This filter provides the capability to enable a feature based on a time window. } ``` -#### Microsoft.Targeting +### Microsoft.Targeting This filter provides the capability to enable a feature for a target audience. An in-depth explanation of targeting is explained in the [targeting](./README.md#Targeting) section below. The filter parameters include an audience object which describes users, groups, excluded users/groups, and a default percentage of the user base that should have access to the feature. Each group object that is listed in the target audience must also specify what percentage of the group's members should have access. If a user is specified in the exclusion section, either directly or if the user is in an excluded group, the feature will be disabled. Otherwise, if a user is specified in the users section directly, or if the user is in the included percentage of any of the group rollouts, or if the user falls into the default rollout percentage then that user will have the feature enabled. @@ -593,7 +598,7 @@ The following steps demonstrate an example of a progressive rollout for a new 'B 5. Five percent of the user base is included in the beta. 6. The rollout percentage is bumped up to 100 percent and the feature is completely rolled out. -This strategy for rolling out a feature is built in to the library through the included [Microsoft.Targeting](./README.md#MicrosoftTargeting) feature filter. +This strategy for rolling out a feature is built-in to the library through the included [Microsoft.Targeting](./README.md#MicrosoftTargeting) feature filter. ### Targeting in a Web Application @@ -612,13 +617,13 @@ The targeting context accessor and `TargetingFilter` are registered by calling ` #### ITargetingContextAccessor -To use the `TargetingFilter` in a web application, an implementation of `ITargetingContextAccessor` is required. This is because when a targeting evaluation is being performed information such as what user is currently being evaluated is needed. This information is known as the targeting context. Different web applications may extract this information from different places. Some common examples of where an application may pull the targeting context are the request's HTTP context or a database. +To use the `TargetingFilter` in a web application, an implementation of `ITargetingContextAccessor` is required. This is because when a targeting evaluation is being performed, information such as what user is currently being evaluated is needed. This information is known as the targeting context. Different web applications may extract this information from different places. Some common examples of where an application may pull the targeting context are the request's HTTP context or a database. An example that extracts targeting context information from the application's HTTP context is included in the [FeatureFlagDemo](./examples/FeatureFlagDemo/HttpContextTargetingContextAccessor.cs) example project. This method relies on the use of `IHttpContextAccessor` which is discussed [here](./README.md#Using-HttpContext). ### Targeting in a Console Application -The targeting filter relies on a targeting context to evaluate whether a feature should be turned on. This targeting context contains information such as what user is currently being evaluated, and what groups the user in. In console applications there is typically no ambient context available to flow this information in to the targeting filter, thus it must be passed directly when `FeatureManager.IsEnabledAsync` is called. This is supported through the use of the `ContextualTargetingFilter`. Applications that need to float the targeting context into the feature manager should use this instead of the `TargetingFilter.` +The targeting filter relies on a targeting context to evaluate whether a feature should be turned on. This targeting context contains information such as what user is currently being evaluated, and what groups the user in. In console applications there is typically no ambient context available to flow this information into the targeting filter, thus it must be passed directly when `FeatureManager.IsEnabledAsync` is called. This is supported through the use of the `ContextualTargetingFilter`. Applications that need to float the targeting context into the feature manager should use this instead of the `TargetingFilter.` Since `ContextualTargetingFilter` is an [`IContextualTargetingFilter`](./README.md#Contextual-Feature-Filters), an implementation of `ITargetingContext` must be passed in to `IFeatureManager.IsEnabledAsync` for it to be able to evaluate and turn a feature on. @@ -674,18 +679,19 @@ When defining an Audience, users and groups can be excluded from the audience. T } ``` -In the above example, the feature will be enabled for users named `Jeff` and `Alicia`. It will also be enabled for users in the group named `Ring0`. However, if the user is named `Mark`, the feature will be disabled, regardless if they are in the group `Ring0` or not. Exclusions take priority over the rest of the targeting filter. +In the above example, the feature will be enabled for users named `Jeff` and `Alicia`. It will also be enabled for users in the group named `Ring0`. However, if the user is named `Mark`, the feature will be disabled, regardless of if they are in the group `Ring0` or not. Exclusions take priority over the rest of the targeting filter. ## Caching Feature state is provided by the IConfiguration system. Any caching and dynamic updating is expected to be handled by configuration providers. The feature manager asks IConfiguration for the latest value of a feature's state whenever a feature is checked to be enabled. ### Snapshot + There are scenarios which require the state of a feature to remain consistent during the lifetime of a request. The values returned from the standard `IFeatureManager` may change if the `IConfiguration` source which it is pulling from is updated during the request. This can be prevented by using `IFeatureManagerSnapshot`. `IFeatureManagerSnapshot` can be retrieved in the same manner as `IFeatureManager`. `IFeatureManagerSnapshot` implements the interface of `IFeatureManager`, but it caches the first evaluated state of a feature during a request and will return the same state of a feature during its lifetime. ## Custom Feature Providers -Implementing a custom feature provider enable developers to pull feature flags from sources such as a database or a feature management service. The included feature provider that is used by default pulls feature flags from .NET Core's configuration system. This allows for features to be defined in an [appsettings.json](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#jcp) file or in configuration providers like [Azure App Configuration](https://docs.microsoft.com/en-us/azure/azure-app-configuration/quickstart-feature-flag-aspnet-core?tabs=core2x). This behavior can be substituted to provide complete control of where feature definitions are read from. +Implementing a custom feature provider enables developers to pull feature flags from sources such as a database or a feature management service. The included feature provider that is used by default pulls feature flags from .NET Core's configuration system. This allows for features to be defined in an [appsettings.json](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#jcp) file or in configuration providers like [Azure App Configuration](https://docs.microsoft.com/en-us/azure/azure-app-configuration/quickstart-feature-flag-aspnet-core?tabs=core2x). This behavior can be substituted to provide complete control of where feature definitions are read from. To customize the loading of feature definitions, one must implement the `IFeatureDefinitionProvider` interface. diff --git a/src/Microsoft.FeatureManagement.AspNetCore/Microsoft.FeatureManagement.AspNetCore.csproj b/src/Microsoft.FeatureManagement.AspNetCore/Microsoft.FeatureManagement.AspNetCore.csproj index fc9fdb1a..99ebd339 100644 --- a/src/Microsoft.FeatureManagement.AspNetCore/Microsoft.FeatureManagement.AspNetCore.csproj +++ b/src/Microsoft.FeatureManagement.AspNetCore/Microsoft.FeatureManagement.AspNetCore.csproj @@ -5,7 +5,7 @@ 3 1 - 0 + 1 diff --git a/src/Microsoft.FeatureManagement/Microsoft.FeatureManagement.csproj b/src/Microsoft.FeatureManagement/Microsoft.FeatureManagement.csproj index 7fcef5f7..88ce8621 100644 --- a/src/Microsoft.FeatureManagement/Microsoft.FeatureManagement.csproj +++ b/src/Microsoft.FeatureManagement/Microsoft.FeatureManagement.csproj @@ -5,7 +5,7 @@ 3 1 - 0 + 1