Skip to content

Commit 7f1660c

Browse files
committed
Adds variant feature flags docs
1 parent cd67eaf commit 7f1660c

File tree

8 files changed

+395
-0
lines changed

8 files changed

+395
-0
lines changed

articles/azure-app-configuration/TOC.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@
158158
href: howto-targetingfilter.md
159159
- name: ASP.NET Core
160160
href: howto-targetingfilter-aspnet-core.md
161+
- name: Use variant feature flags
162+
items:
163+
- name: Overview
164+
href: use-variant-feature-flags.md
165+
- name: ASP.NET Core
166+
href: use-variant-feature-flags-aspnet-core.md
161167
- name: Run experiments with variant feature flags
162168
items:
163169
- name: ASP.NET Core

articles/azure-app-configuration/index.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ landingContent:
162162
url: howto-timewindow-filter.md
163163
- text: Roll out features to targeted audience
164164
url: howto-targetingfilter.md
165+
- text: Use variant feature flags
166+
url: use-variant-feature-flags.md
165167
- text: Run experiments with variant feature flags
166168
url: run-experiments-aspnet-core.md
167169
- linkListType: reference
19.7 KB
Loading
37.3 KB
Loading
37.2 KB
Loading
47.1 KB
Loading
Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
---
2+
title: 'Tutorial: Use variant feature flags from Azure App Configuration in an ASP.NET application'
3+
titleSuffix: Azure App configuration
4+
description: In this tutorial, you learn how to use variant feature flags in an ASP.NET application
5+
#customerintent: As a user of Azure App Configuration, I want to learn how I can use variants and variant feature flags in my ASP.NET application.
6+
author: rossgrambo
7+
ms.author: rossgrambo
8+
ms.service: azure-app-configuration
9+
ms.devlang: csharp
10+
ms.topic: tutorial
11+
ms.date: 10/10/2024
12+
---
13+
14+
# Tutorial: Use variant feature flags from Azure App Configuration in an ASP.NET application
15+
16+
In this tutorial, you'll use a variant feature flag to manage experiences for different user segments in an example application, *Quote of the Day*. You'll utilize the variant feature flag created in [Use variant feature flags](./use-variant-feature-flags.md). Before proceeding, ensure you create the variant feature flag named *Greeting* in your App Configuration store.
17+
18+
> [!div class="checklist"]
19+
> * Set up an ASP.NET app to consume variant feature flags
20+
21+
## Prerequisites
22+
23+
* An Azure subscription. If you don’t have one, [create one for free](https://azure.microsoft.com/free/).
24+
* An [App Configuration store](./quickstart-azure-app-configuration-create.md).
25+
* [Use variant feature flags](./use-variant-feature-flags.md)
26+
27+
### Create an ASP.NET Core web app
28+
29+
1. Open a command prompt and run the following code. This creates a new Razor Pages application in ASP.NET Core, using Individual account auth, and places it in an output folder named *QuoteOfTheDay*.
30+
31+
```dotnetcli
32+
dotnet new razor --auth Individual -o QuoteOfTheDay
33+
```
34+
35+
1. In the command prompt, navigate to the *QuoteOfTheDay* folder and run the following command to create a [user secret](/aspnet/core/security/app-secrets) for the application. This secret holds the endpoint for App Configuration.
36+
37+
```dotnetcli
38+
dotnet user-secrets set Endpoints:AppConfiguration "<App Configuration Endpoint>"
39+
```
40+
41+
1. Add the latest versions of the required libraries.
42+
43+
```dotnetcli
44+
dotnet add package Azure.Identity
45+
dotnet add package Microsoft.Extensions.Configuration.AzureAppConfiguration
46+
dotnet add package Microsoft.FeatureManagement.AspNetCore
47+
```
48+
49+
## Connect to App Configuration for feature management
50+
51+
1. In *Program.cs*, under the line `var builder = WebApplication.CreateBuilder(args);`, add the App Configuration provider, which pulls down the configuration from Azure App Configuration when the application starts. See the [.NET provider quickstart](./quickstart-dotnet-core-app?tabs=entra-id#connect-to-an-app-configuration-store) for more on authenticating with the provider. By default, the UseFeatureFlags method includes all feature flags with no label.
52+
53+
```csharp
54+
builder.Configuration
55+
.AddAzureAppConfiguration(options =>
56+
{
57+
string endpoint = builder.Configuration.Get("Endpoints:AppConfiguration");
58+
options.Connect(new Uri(endpoint), new DefaultAzureCredential());
59+
60+
options.UseFeatureFlags();
61+
});
62+
```
63+
64+
1. In *Program.cs*, add the following using statements.
65+
66+
```csharp
67+
using Microsoft.FeatureManagement;
68+
```
69+
70+
1. Add Azure App Configuration and feature management services and enable targeting for feature management.
71+
72+
```csharp
73+
// Add Azure App Configuration and feature management services to the container.
74+
builder.Services.AddAzureAppConfiguration()
75+
.AddFeatureManagement()
76+
.WithTargeting();
77+
```
78+
79+
1. Under the line `var app = builder.Build();`, add Azure App Configuration middleware for dynamic configuration refresh.
80+
81+
```csharp
82+
// Use Azure App Configuration middleware for dynamic configuration refresh.
83+
app.UseAzureAppConfiguration();
84+
```
85+
86+
## Use the variant feature flag
87+
88+
1. In *QuoteOfTheDay* > *Pages* > *Shared* > *_Layout.cshtml*, under where `QuoteOfTheDay.styles.css` is added, add the following reference to the font-awesome CSS library.
89+
90+
```css
91+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
92+
```
93+
94+
1. Open *QuoteOfTheDay* > *Pages* > *Index.cshtml.cs* and replace the content with the following code.
95+
96+
```csharp
97+
using Microsoft.AspNetCore.Mvc;
98+
using Microsoft.AspNetCore.Mvc.RazorPages;
99+
using Microsoft.FeatureManagement;
100+
101+
namespace QuoteOfTheDay.Pages;
102+
103+
public class Quote
104+
{
105+
public string Message { get; set; }
106+
107+
public string Author { get; set; }
108+
}
109+
110+
public class IndexModel(IVariantFeatureManagerSnapshot featureManager) : PageModel
111+
{
112+
private readonly IVariantFeatureManagerSnapshot _featureManager = featureManager;
113+
114+
private Quote[] _quotes = [
115+
new Quote()
116+
{
117+
Message = "You cannot change what you are, only what you do.",
118+
Author = "Philip Pullman"
119+
}];
120+
121+
public Quote? Quote { get; set; }
122+
123+
public string GreetingMessage { get; set; }
124+
125+
public async void OnGet()
126+
{
127+
Quote = _quotes[new Random().Next(_quotes.Length)];
128+
129+
Variant variant = await _featureManager.GetVariantAsync("Greeting", HttpContext.RequestAborted);
130+
131+
if (variant != null)
132+
{
133+
GreetingMessage = variant.Configuration?.Get<string>() ?? "";
134+
}
135+
else
136+
{
137+
_logger.LogWarning("Greeting variant not found. Please define a variant feature flag in Azure App Configuration named 'Greeting'.");
138+
}
139+
}
140+
141+
public IActionResult OnPostHeartQuoteAsync()
142+
{
143+
string? userId = User.Identity?.Name;
144+
145+
if (!string.IsNullOrEmpty(userId))
146+
{
147+
return new JsonResult(new { success = true });
148+
}
149+
else
150+
{
151+
return new JsonResult(new { success = false, error = "User not authenticated" });
152+
}
153+
}
154+
}
155+
```
156+
157+
This `PageModel` picks a random quote, uses `GetVariantAsync` to get the variant for the current user, and sets a variable called "GreetingMessage" to the variant's value. The `PageModel` also handles likes, which are sent as post requests.
158+
159+
1. Open *index.cshtml* and replace its content with the following code.
160+
161+
```cshtml
162+
@page
163+
@model IndexModel
164+
@{
165+
ViewData["Title"] = "Home page";
166+
ViewData["Username"] = User.Identity?.Name ?? string.Empty;
167+
}
168+
169+
<style>
170+
body {
171+
font-family: Arial, sans-serif;
172+
background-color: #f4f4f4;
173+
color: #333;
174+
}
175+
176+
.quote-container {
177+
background-color: #fff;
178+
margin: 2em auto;
179+
padding: 2em;
180+
border-radius: 8px;
181+
max-width: 750px;
182+
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2);
183+
display: flex;
184+
justify-content: space-between;
185+
align-items: start;
186+
position: relative;
187+
}
188+
189+
.vote-container {
190+
position: absolute;
191+
top: 10px;
192+
right: 10px;
193+
display: flex;
194+
gap: 0em;
195+
}
196+
197+
.vote-container .btn {
198+
background-color: #ffffff; /* White background */
199+
border-color: #ffffff; /* Light blue border */
200+
color: #333
201+
}
202+
203+
.vote-container .btn:focus {
204+
outline: none;
205+
box-shadow: none;
206+
}
207+
208+
.vote-container .btn:hover {
209+
background-color: #F0F0F0; /* Light gray background */
210+
}
211+
212+
.greeting-content {
213+
font-family: 'Georgia', serif; /* More artistic font */
214+
}
215+
216+
.quote-content p.quote {
217+
font-size: 2em; /* Bigger font size */
218+
font-family: 'Georgia', serif; /* More artistic font */
219+
font-style: italic; /* Italic font */
220+
color: #4EC2F7; /* Medium-light blue color */
221+
}
222+
</style>
223+
224+
<div class="quote-container">
225+
<div class="quote-content">
226+
<h3 class="greeting-content">@(Model.GreetingMessage)</h3>
227+
<br />
228+
<p class="quote">“@(Model.Quote?.Message ?? "< Quote not found >")”</p>
229+
<p>- <b>@(Model.Quote?.Author ?? "Unknown")</b></p>
230+
</div>
231+
232+
<div class="vote-container">
233+
<button class="btn btn-primary" onclick="heartClicked(this)">
234+
<i class="far fa-heart"></i> <!-- Heart icon -->
235+
</button>
236+
</div>
237+
238+
<form action="/" method="post">
239+
@Html.AntiForgeryToken()
240+
</form>
241+
</div>
242+
243+
<script>
244+
function heartClicked(button) {
245+
var icon = button.querySelector('i');
246+
icon.classList.toggle('far');
247+
icon.classList.toggle('fas');
248+
249+
// If the quote is hearted
250+
if (icon.classList.contains('fas')) {
251+
// Send a request to the server to save the vote
252+
fetch('/Index?handler=HeartQuote', {
253+
method: 'POST',
254+
headers: {
255+
'Content-Type': 'application/json',
256+
'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
257+
}
258+
});
259+
}
260+
}
261+
</script>
262+
```
263+
264+
This code corresponds to the UI to show the QuoteOfTheDay and handle using the heart action on a quote. It uses the previously mentioned `Model.GreetingMessage` value to show different things to different users, depending on their variant.
265+
266+
### Build and run the app
267+
268+
1. Build and run the application.
269+
270+
```dotnetcli
271+
dotnet build
272+
dotnet run
273+
```
274+
1. Once the application is loaded, select **Register** at the top right to register a new user.
275+
276+
:::image type="content" source="media/use-variant-feature-flags-aspnet-core/register.png" alt-text="Screenshot of the Quote of the day app, showing Register.":::
277+
278+
1. Register a new user named *[email protected]*. The password must have at least six characters and contain a number and a special character.
279+
280+
1. Select the link **Click here to validate email** after entering user information.
281+
282+
:::image type="content" source="media/use-variant-feature-flags-aspnet-core/click-to-confirm.png" alt-text="Screenshot of the Quote of the day app, showing click to confirm.":::
283+
284+
1. Repeat the same steps to register a second user named [email protected].
285+
286+
> [!NOTE]
287+
> It's important for the purpose of this tutorial to use these names exactly. As long as the feature has been configured as expected, the two users should see different variants.
288+
@
289+
1. Select **Login** at the top right to sign in as [email protected].
290+
291+
:::image type="content" source="media/use-variant-feature-flags-aspnet-core/login.png" alt-text="Screenshot of the Quote of the day app, showing **Login**.":::
292+
293+
1. Once logged in, you should see that [email protected] sees the long message when viewing the app, and [email protected] sees the simple message.
294+
295+
:::image type="content" source="media/use-variant-feature-flags-aspnet-core/special-message.png" alt-text="Screenshot of the Quote of the day app, showing a special message for the user.":::
296+
297+
## Next steps
298+
299+
To learn more about the experimentation concepts, refer to the following document.
300+
301+
> [!div class="nextstepaction"]
302+
> [Experimentation](./concept-experimentation.md)
303+
304+
For the full feature rundown of the .NET feature management library, refer to the following document.
305+
306+
> [!div class="nextstepaction"]
307+
> [.NET Feature Management](./feature-management-dotnet-reference.md)

0 commit comments

Comments
 (0)