你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

教程:在 ASP.NET Core 应用程序中使用变体功能标志

在本教程中,你将使用变体功能标志来管理示例应用程序中不同用户段的体验,今日名言。 利用在使用变体功能标志创建的变体功能标志。 在继续操作之前,请确保在应用程序配置存储中创建名为 Greeting 的变体功能标志。

先决条件

创建一个 ASP.NET Core Web 应用

  1. 在命令提示符中运行以下代码。 此命令将使用个人帐户身份验证在 ASP.NET Core 中创建新的 Razor Pages 应用程序,并将其置于名为 QuoteOfTheDay 的输出文件夹中。

    dotnet new razor --auth Individual -o QuoteOfTheDay
    
  2. 导航到 QuoteOfTheDay 目录,通过运行以下命令为应用程序创建用户机密。 将 <your-App-Configuration-endpoint> 占位符替换为应用程序配置存储区的终结点。 在 Azure 门户中,可以在应用程序配置存储区的“概览”边栏选项卡中找到终结点。

    dotnet user-secrets init
    dotnet user-secrets set Endpoints:AppConfiguration "<your-App-Configuration-endpoint>"
    
  3. 添加所需包的最新版本。

    dotnet add package Azure.Identity
    dotnet add package Microsoft.Extensions.Configuration.AzureAppConfiguration
    dotnet add package Microsoft.FeatureManagement.AspNetCore
    

连接到应用程序配置进行功能管理

  1. 打开 Program.cs 并添加以下 using 语句

    using Azure.Identity;
    using Microsoft.Extensions.Configuration.AzureAppConfiguration;
    using Microsoft.FeatureManagement;
    
  2. 添加以下代码以连接到应用程序配置存储区,并调用 UseFeatureFlags 以拉取所有没有标签的功能标志。

    可以使用 DefaultAzureCredential 向应用程序配置存储区进行身份验证。 按照说明为凭据分配应用程序配置数据读取者角色。 在运行应用程序之前,请务必留出足够的时间来传播权限。

    var builder = WebApplication.CreateBuilder(args); 
    
    // Retrieve the endpoint
    string endpoint = builder.Configuration.GetValue<string>("Endpoints:AppConfiguration")
        ?? throw new InvalidOperationException("The setting `Endpoints:AppConfiguration` was not found.");
    
    // Load configuration and feature flags from Azure App Configuration
    builder.Configuration
        .AddAzureAppConfiguration(options =>
        {
            options.Connect(new Uri(endpoint), new DefaultAzureCredential())
                   .UseFeatureFlags();
        });
    
  3. 添加 Azure 应用程序配置和功能管理服务,并为功能管理启用目标。

    // Add Azure App Configuration and feature management services to the container.
    builder.Services.AddAzureAppConfiguration()
        .AddFeatureManagement()
        .WithTargeting();
    
  4. 在行 var app = builder.Build();下,添加 Azure 应用程序置中间件进行动态配置刷新。

    // Use Azure App Configuration middleware for dynamic configuration refresh.
    app.UseAzureAppConfiguration();
    

使用变体功能标志

  1. 打开 QuoteOfTheDay>Pages>Index.cshtml.cs ,并将内容替换为以下代码。

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.RazorPages;
    using Microsoft.FeatureManagement;
    
    namespace QuoteOfTheDay.Pages;
    
    public class Quote
    {
        public string Message { get; set; }
    
        public string Author { get; set; }
    }
    
    public class IndexModel(IVariantFeatureManagerSnapshot featureManager) : PageModel
    {
        private readonly IVariantFeatureManagerSnapshot _featureManager = featureManager;
    
        private Quote[] _quotes = [
            new Quote()
            {
                Message = "You cannot change what you are, only what you do.",
                Author = "Philip Pullman"
            }];
    
        public Quote? Quote { get; set; }
    
        public string GreetingMessage { get; set; }
    
        public async void OnGet()
        {
            Quote = _quotes[new Random().Next(_quotes.Length)];
    
            Variant variant = await _featureManager.GetVariantAsync("Greeting", HttpContext.RequestAborted);
    
            if (variant != null)
            {
                GreetingMessage = variant.Configuration?.Get<string>() ?? "";
            }
            else
            {
                _logger.LogWarning("No variant given. Either the feature flag named 'Greeting' is not defined or the variants are not defined properly.");
            }
        }
    }
    

    调用 GetVariantAsync 检索当前用户的 Greeting 功能标志的变体,并将其值分配给页面模型的 GreetingMessage 属性。

  2. 在 QuoteOfTheDay>Pages>Shared>_Layout.cshtml中,在添加 QuoteOfTheDay.styles.css 处的下方,添加以下对 font-awesome CSS 库的引用。

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
    
  3. 打开 Index.cshtml,并将其内容替换为以下代码。

    @page
    @model IndexModel
    @{
        ViewData["Title"] = "Home page";
        ViewData["Username"] = User.Identity?.Name ?? string.Empty;
    }
    
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
            color: #333;
        }
    
        .quote-container {
            background-color: #fff;
            margin: 2em auto;
            padding: 2em;
            border-radius: 8px;
            max-width: 750px;
            box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2);
            display: flex;
            justify-content: space-between;
            align-items: start;
            position: relative;
        }
    
        .vote-container {
            position: absolute;
            top: 10px;
            right: 10px;
            display: flex;
            gap: 0em;
        }
    
        .vote-container .btn {
            background-color: #ffffff; /* White background */
            border-color: #ffffff; /* Light blue border */
            color: #333
        }
    
        .vote-container .btn:focus {
            outline: none;
            box-shadow: none;
        }
    
        .vote-container .btn:hover {
            background-color: #F0F0F0; /* Light gray background */
        }
    
        .greeting-content {
            font-family: 'Georgia', serif; /* More artistic font */
        }
    
        .quote-content p.quote {
            font-size: 2em; /* Bigger font size */
            font-family: 'Georgia', serif; /* More artistic font */
            font-style: italic; /* Italic font */
            color: #4EC2F7; /* Medium-light blue color */
        }
    </style>
    
    <div class="quote-container">
        <div class="quote-content">
            <h3 class="greeting-content">@(Model.GreetingMessage)</h3>
            <br />
            <p class="quote">“@(Model.Quote?.Message ?? "< Quote not found >")”</p>
            <p>- <b>@(Model.Quote?.Author ?? "Unknown")</b></p>
        </div>
    
        <div class="vote-container">
            <button class="btn btn-primary" onclick="heartClicked(this)">
                <i class="far fa-heart"></i> <!-- Heart icon -->
            </button>
        </div>
    </div>
    
    <script>
        function heartClicked(button) {
            var icon = button.querySelector('i');
            icon.classList.toggle('far');
            icon.classList.toggle('fas');
        }
    </script>
    

    此代码显示今日名言应用程序的 UI,并显示页面模型中的 GreetingMessage。 单击红心按钮时,将触发 JavaScript 处理程序 heartClicked

生成并运行应用

  1. 生成并运行应用程序。

    dotnet build
    dotnet run
    
  2. 加载应用程序后,选择右上角注册以注册新用户。

    “今日名言”应用的屏幕截图,其中显示了“注册”。

  3. 注册名为 usera@contoso.com 的新用户。

  4. 在输入用户信息后,选择“单击此处验证电子邮件”。

    “今日名言”应用的屏幕截图,其中显示了单击以确认。

  5. 重复相同的步骤,注册名为 userb@contoso.com 的第二个用户。

    注意

    对于本教程而言,准确使用这些名称非常重要。 只要该功能已按预期进行配置,两个用户应会看到不同的变体。

  6. 选择右上角的“登录”以 usera@contoso.com 身份登录。

    “今日名言”应用的屏幕截图,显示**登录**。

  7. 登录后,会看到对 usera@contoso.com 的长问候语消息

    “今日名言”应用的屏幕截图,为用户显示了一条长消息。

  8. 单击“注销并作为 userb@contoso.com 登录,会看到简单的问候消息。

    “今日名言”应用的屏幕截图,为用户显示了一条简单消息。

后续步骤

有关 .NET 功能管理库的完整功能概要,请参阅以下文档。