Phần 4 - Tạo bộ lọc tính năng tùy chỉnh

Đây là bài thứ 4 trong loạt bài: Thêm cờ tính năng vào ứng dụng ASP.NET Core.

Microsoft.FeatureManagement cho phép bạn thêm các cờ tính năng vào ứng dụng ASP.NET Core được kiểm soát bởi hệ thống cấu hình. Trong bài viết trước, tôi đã giới thiệu các bộ lọc tính năng và chỉ ra cách chúng có thể được sử dụng để tạo cờ tính năng động. Tôi đã mô tả hai bộ lọc tính năng được tích hợp sẵn trong thư viện TimeWindowFilterPercentageFilter.

Trong bài viết này, tôi chỉ cho bạn cách bạn có thể tạo bộ lọc tính năng tùy chỉnh của riêng mình. Ví dụ trong bài viết này tìm kiếm Claim trong ClaimsPrincipal của người dùng hiện đang đăng nhập và bật cờ tính năng nếu có. Bạn có thể sử dụng bộ lọc này để kích hoạt một tính năng cho một nhóm nhỏ người dùng của bạn.

Tạo bộ lọc tùy chỉnh từ IFeatureFilter

Bài viết này giả định rằng bạn đã quen thuộc với các bộ lọc tính năng và thư viện Microsoft.FeatureManagement nói chung, vì vậy nếu chúng là người mới đối với bạn, tôi khuyên bạn nên đọc các bài viết trước trong loạt bài này.

Tạo bộ lọc tính năng tùy chỉnh yêu cầu hai điều:

  • Tạo một lớp có nguồn gốc từ interface IFeatureFilter.
  • Tùy chọn: tạo một lớp cài đặt để kiểm soát bộ lọc tính năng của bạn.

Tôi sẽ bắt đầu với lớp cài đặt bộ lọc, vì chúng ta sẽ sử dụng lớp đó bên trong bộ lọc tính năng tùy chỉnh của mình.

Tạo lớp cài đặt bộ lọc

Đối với ví dụ này, chúng ta muốn bật một tính năng chỉ cho những người dùng có một số xác nhận quyền sở hữu (claim) nhất định. Để đơn giản, tôi sẽ chỉ yêu cầu sự hiện diện của một loại xác nhận quyền sở hữu và bỏ qua giá trị của xác nhận quyền sở hữu, nhưng mở rộng ví dụ trong bài viết này phải đủ đơn giản. Đối tượng cài đặt chứa một loạt các loại xác nhận quyền sở hữu:

public class ClaimsFilterSettings
{
    public string[] RequiredClaims { get; set; }
}

Không có gì đặc biệt về lớp cài đặt này; nó sẽ bị ràng buộc với cấu hình ứng dụng của bạn, vì vậy bạn chỉ bị hạn chế bởi các giới hạn của cấu hình ASP.NET Core / Microsoft.Extensions tiêu chuẩn.

Triển khai interface IFeatureFilter

Để tạo bộ lọc tính năng, bạn phải triển khai interface IFeatureFilter, bao gồm một phương thức duy nhất:

public interface IFeatureFilter
{
    bool Evaluate(FeatureFilterEvaluationContext context);
}

Đối số FeatureFilterEvaluationContext được truyền vào phương thức chứa tên của đối tượng được yêu cầu và một đối tượng IConfiguration cho phép bạn truy cập cài đặt cho đối tượng:

public class FeatureFilterEvaluationContext
{
    public string FeatureName { get; set; }
    public IConfiguration Parameters { get; set; }
}
Cần lưu ý rằng không có gì cụ thể cho ASP.NET Core ở đây - không có HttpContext và không có IServiceProvider. May mắn thay, lớp của bạn được inject từ DI Container, vì vậy bạn sẽ có thể nhận được mọi thứ bạn cần trong phương thức khởi tạo của bộ lọc tính năng của bạn.

Tạo bộ lọc tính năng tùy chỉnh

Để triển khai bộ lọc tính năng tùy chỉnh, chúng ta cần biết người dùng hiện tại của yêu cầu là ai. Để làm như vậy, chúng ta cần truy cập vào HttpContext.

Cách chính xác để làm điều đó (khi bạn không có quyền truy cập trực tiếp vào nó như khi bạn làm trong Controller MVC, v.v.) là sử dụng interface IHttpContextAccessor.

Bên trong class ClaimsFeatureFilter có một tham số là IHttpContextAccessor trong hàm khởi tạo của nó và sử dụng HttpContext để truy xuất người dùng hiện tại từ yêu cầu.

[FilterAlias("Claims")] // How we will refer to the filter in configuration
public class ClaimsFeatureFilter : IFeatureFilter
{
    // Used to access HttpContext
    private readonly IHttpContextAccessor _httpContextAccessor;
    public ClaimsFeatureFilter(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public bool Evaluate(FeatureFilterEvaluationContext context)
    {
        // Get the ClaimsFilterSettings from configuration
        var settings = context.Parameters.Get<ClaimsFilterSettings>();

        // Retrieve the current user (ClaimsPrincipal)
        var user = _httpContextAccessor.HttpContext.User;

        // Only enable the feature if the user has ALL the required claims
        var isEnabled = settings.RequiredClaims
            .All(claimType => user.HasClaim(claim => claim.Type == claimType));

        return isEnabled;
    }
}

Tôi đã đặt tên cho bộ lọc tính năng này là "Claims" bằng cách sử dụng thuộc tính [FilterAlias]. Đây là chuỗi bạn cần thêm vào cấu hình để kích hoạt bộ lọc, như bạn sẽ thấy ngay sau đây. Bạn có thể truy xuất ClaimsFilterSettings liên kết với một instance nhất định của bộ lọc tính năng tùy chỉnh bằng cách gọi phương thức context.Parameters.Get<>().

Logic của bộ lọc tương đối đơn giản - nếu ClaimsPrincipal cho yêu cầu có tất cả các xác nhận quyền sở hữu được yêu cầu, thì tính năng được liên kết sẽ được bật, nếu không thì tính năng này sẽ bị vô hiệu hóa.

Sử dụng bộ lọc tính năng tùy chỉnh

Để sử dụng bộ lọc tính năng tùy chỉnh, bạn phải đăng ký nó với hệ thống quản lý tính năng trong Startup.ConfigureServices(). Chúng ta cũng cần đảm bảo rằng interface IHttpContextAccessor có sẵn trong DI Container:

using Microsoft.FeatureManagement;

public class Startup 
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Add IHttpContextAccessor if it's not yet added
        services.AddHttpContextAccessor();

        services.AddFeatureManagement()
            .AddFeatureFilter<ClaimsFeatureFilter>(); // add our custom filter
    }
}
Tùy thuộc vào framework và các dịch vụ của bên thứ ba mà bạn đã thêm vào ứng dụng của mình, IHttpContextAccessor có thể đã có sẵn. Tuy nhiên, an toàn khi gọi AddHttpContextAccessor() nhiều lần, rất đáng để đề phòng.

Đó là tất cả cấu hình tùy chỉnh cần thiết để kích hoạt ClaimsFeatureFilter của chúng ta. Để thực sự sử dụng nó trong một ứng dụng, chúng ta sẽ thêm một cờ tính năng có tên "Beta":

public static class FeatureFlags
{
    public const string Beta = "Beta";
}

và bật bộ lọc trong cấu hình bằng cách sử dụng định dạng được mô tả trong bài trước:

{
  "FeatureManagement": 
  {
    "Beta": 
    {
      "EnabledFor": 
      [
        {
          "Name": "Claims",
          "Parameters": 
          {
            "RequiredClaims": [ "Internal" ]
          }
        }
      ]
    }
  }
}
Lưu ý rằng tôi đã sử dụng giá trị "Claims" của [FilterAlias] cho thuộc tính Name của bộ lọc. Đối tượng Parameters tương ứng với đối tượng cài đặt ClaimsFilterSettings. Với cấu hình này, người dùng có xác nhận quyền sở hữu "Internal" sẽ được bật cờ tính năng Beta - người dùng khác sẽ thấy nó bị vô hiệu hóa.

Kiểm tra bộ lọc ClaimsFeatureFilter

Để kiểm tra bộ lọc tính năng, dễ nhất là bắt đầu với ứng dụng ASP.NET Core đã bật xác thực cá nhân. Với mục đích trình diễn, tôi đã cập nhật trang chủ Index.cshtml để hiển thị banner khi cờ tính năng Beta được bật bằng FeatureTagHelper :

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<!-- Only visible when Beta feature flag is enabled -->
<feature name="@FeatureFlags.Beta">
    <div class="alert alert-primary" role="alert">
        Congratulations - You're in the Beta test!
    </div>
</feature>

<!-- ... -->

Chạy lệnh di chuyển cơ sở dữ liệu cho ứng dụng bằng cách sử dụng lệnh sau:

dotnet ef database update

và sau đó chạy ứng dụng của bạn và đăng ký người dùng mới. Bạn sẽ thấy trang chủ tiêu chuẩn:

Tạo bộ lọc tùy chỉnh từ IFeatureFilter

Banner "Beta" bị ẩn theo mặc định. Bộ lọc ClaimsFeatureFilter của chúng ta đã kiểm tra xác nhận quyền sở hữu của người dùng đối với xác nhận quyền sở hữu bắt buộc "Internal", không có theo mặc định và được trả lại false khi gọi phương thức IsEnabled() của bộ lọc.

Để kích hoạt tính năng này, chúng ta cần thêm một xác nhận quyền sở hữu bổ sung cho người dùng.

Có một số cách để thêm xác nhận quyền sở hữu cho người dùng - khi người dùng được tạo, sau đó hoặc khi họ đăng nhập. Tôi đang đi theo con đường dễ dàng ở đây và chỉ cần thêm xác nhận quyền sở hữu vào cơ sở dữ liệu theo cách thủ công.

Với ASP.NET Core Identity, người dùng có thể thêm các xác nhận quyền sở hữu bổ sung tùy ý. Chúng được lưu trữ trong bảng AspNetUserClaims (theo mặc định). Tôi đã sử dụng Server Explorer của Visual Studio để thêm một hàng mới trong bảng này được liên kết với người dùng của tôi, sử dụng loại xác nhận quyền sở hữu "Internal".

Tạo bộ lọc tùy chỉnh từ IFeatureFilter

Nếu bạn đăng xuất và sau đó đăng nhập lại (để đảm bảo xác nhận quyền sở hữu mới được nhận), banner "Beta" sẽ hiện hiển thị - bộ lọc tính năng tùy chỉnh đã hoạt động!

Tạo bộ lọc tùy chỉnh từ IFeatureFilter

Hạn chế của bộ lọc ClaimsFeatureFilter

Bộ lọc tính năng tùy chỉnh được ClaimsFeatureFilter mô tả trong bài viết này chỉ nhằm mục đích làm ví dụ về bộ lọc bạn có thể sử dụng. Việc dựa vào HttpContext mang lại cho nó một giới hạn cụ thể: nó không thể được sử dụng bên ngoài ngữ cảnh của một yêu cầu HTTP.

Cố gắng truy cập HttpContext bên ngoài một yêu cầu HTTP có thể dẫn đến NullReferenceException. Bạn cũng cần phải cẩn thận khi sử dụng nó trong một background thread, HttpContext không an toàn cho một thread.

Một trong những tác động hơi nguy hiểm của điều này là người sử dụng (dev) cờ tính năng không biết tính năng nào là an toàn để truy vấn trong bối cảnh nào. Không có gì trong đoạn mã cho thấy nó có thể ném ra exception khi được sử dụng trong một background thread hoặc trong một dịch vụ được lưu trữ.

var isEnabled = _featureManager.IsEnabled(FeatureFlags.Beta); // may throw!

Một tùy chọn cơ bản để tránh tình huống này là sử dụng các quy ước đặt tên cho cờ đặc trưng của bạn. Ví dụ: bạn có thể sử dụng một quy ước trong đó cờ tính năng có tiền tố "UI_" chỉ được coi là "an toàn" để truy cập khi có ngữ cảnh yêu cầu HTTP.

public static class FeatureFlags
{
    // These flags are safe to access in any context
    public const string NewBranding = "NewBranding";
    public const string AlternativeColours = "AlternativeColours";

    // These flags are only safe to access from an HttpContext-safe request
    public static class Ui
    {
      const string _prefix = "UI_";
      public const string Beta = _prefix + "Beta";
      public const string NewOnboardingExperiences = _prefix + "NewOnboardingExperiences";
    }
}

Điều này ít nhất cung cấp một chỉ báo cho người gọi khi cờ được sử dụng. Rõ ràng là nó yêu cầu bạn cấu hình các cờ một cách chính xác, nhưng đó là một bước đi đúng hướng!

// Flags on the main FeatureFlags class are safe to use everywhere
var isEnabled = _featureManager.IsEnabled(FeatureFlags.NewBranding); 

// Flags on the nested Ui class are only safe when HttpContext is available
var isEnabled = _featureManager.IsEnabled(FeatureFlags.Ui.Beta); 

Tóm lược

Microsoft.FeatureManagement cho phép sử dụng bộ lọc tính năng để thêm hành vi động vào cờ tính năng của bạn. Trong bài viết này, tôi đã chỉ ra cách triển khai interface IFeatureFilter tùy chỉnh của riêng bạn, sử dụng xác nhận quyền sở hữu của người dùng hiện tại để quyết định xem có nên bật cờ hay không.

Bộ lọc tính năng này hoạt động tốt trong ngữ cảnh của một yêu cầu (request), nhưng điều quan trọng là phải nhận thức được ý nghĩa của việc sử dụng HttpContext liên quan đến việc sử dụng cờ tính năng trong background thread và bên ngoài ngữ cảnh HTTP.

Series: Thêm cờ tính năng vào ứng dụng ASP.NET Core
Trong loạt bài này, tôi sẽ trình bày cách tạo các cờ tính năng vào ứng dụng ASP.NET Core sử dụng thư viện Microsoft.FeatureManagement.
ASP.NET Core.NET CoreASP.NET Core MVCLập Trình C#
Bài Viết Liên Quan:
Phần 6 - Các lựa chọn thay thế cho Microsoft.FeatureManagement
Trung Nguyen 13/03/2022
Phần 6 - Các lựa chọn thay thế cho Microsoft.FeatureManagement

Trong bài viết này, tôi giới thiệu sơ lược về một số lựa chọn thay thế cho thư viện Microsoft.FeatureManagement và mô tả sự khác biệt của chúng

Phần 5 - Đảm bảo cờ tính năng nhất quán trên các yêu cầu
Trung Nguyen 13/03/2022
Phần 5 - Đảm bảo cờ tính năng nhất quán trên các yêu cầu

Trong bài viết này, tôi giới thiệu hai cách để cải thiện tính nhất quán của cờ tính năng cho mọi yêu cầu đối với người dùng trong ứng dụng ASP.NET Core.

Phần 3 - Tạo cờ tính năng động với bộ lọc tính năng
Trung Nguyen 12/03/2022
Phần 3 - Tạo cờ tính năng động với bộ lọc tính năng

Trong bài này, tôi sẽ hướng dẫn cách sử dụng hai bộ lọc PercentageFilter và TimeWindowFilter để tạo cờ tính năng động trong ứng dụng ASP.NET Core.

Phần 2 - Lọc các action method với cờ tính năng
Trung Nguyen 12/03/2022
Phần 2 - Lọc các action method với cờ tính năng

Trong bài này, tôi giới thiệu thư viện Microsoft.FeatureManagement.AspNetCore bổ sung các tính năng dành riêng cho ASP.NET Core để làm việc với cờ tính năng