在本教學課程中,您使用變體功能旗標來管理範例應用程式 Quote of the Day 中不同使用者區隔的體驗。 您利用使用變體功能旗標 (部分機器翻譯) 中所建立的變體功能旗標。 繼續之前,請確定您在應用程式組態存放區中建立名為 Greeting 的變體功能旗標。
先決條件
- 具有有效訂閱的 Azure 帳戶。 免費創建一個。
- 應用程式組態存放區,如 建立存放區指引所示。
- 轉到 1.21 或更高版本。 如需安裝 Go 的相關資訊,請參閱 Go 下載頁面。
- Azure App Configuration Go 提供者 v1.1.0 或更新版本。
- 功能管理 Go 程式庫 v1.1.0 或更新版本。
- 請遵循使用變體功能旗標 (部分機器翻譯) 教學課程,然後建立名為 Greeting 的變體功能旗標。
設定 Go Gin Web 應用程式
為您的 Go 專案建立一個新目錄並導航到其中:
mkdir quote-of-the-day cd quote-of-the-day初始化一個新的Go模組:
go mod init quote-of-the-day安裝必要的 Go 套件:
go get github.com/gin-gonic/gin go get github.com/gin-contrib/sessions go get github.com/gin-contrib/sessions/cookie go get github.com/microsoft/Featuremanagement-Go/featuremanagement go get github.com/microsoft/Featuremanagement-Go/featuremanagement/providers/azappconfig為您的HTML範本建立範本目錄,並新增所需的HTML檔案:
mkdir templates從 GitHub 存放庫 新增下列 HTML 範本檔案,並將其放在目錄中
templates:-
index.html- 首頁範本 -
login.html- 登入頁面範本
-
建立具有下列內容的檔案
appconfig.go。 您可以使用 Microsoft Entra ID (建議) 或連接字串連線到應用程式組態存放區。package main import ( "context" "log" "os" "github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" ) func loadAzureAppConfiguration(ctx context.Context) (*azureappconfiguration.AzureAppConfiguration, error) { // Get the endpoint from environment variable endpoint := os.Getenv("AZURE_APPCONFIG_ENDPOINT") if endpoint == "" { log.Fatal("AZURE_APPCONFIG_ENDPOINT environment variable is not set") } // Create a credential using DefaultAzureCredential credential, err := azidentity.NewDefaultAzureCredential(nil) if err != nil { log.Fatalf("Failed to create credential: %v", err) } // Set up authentication options with endpoint and credential authOptions := azureappconfiguration.AuthenticationOptions{ Endpoint: endpoint, Credential: credential, } // Set up options to enable feature flags options := &azureappconfiguration.Options{ FeatureFlagOptions: azureappconfiguration.FeatureFlagOptions{ Enabled: true, RefreshOptions: azureappconfiguration.RefreshOptions{ Enabled: true, }, }, } // Load configuration from Azure App Configuration appConfig, err := azureappconfiguration.Load(ctx, authOptions, options) if err != nil { log.Fatalf("Failed to load configuration: %v", err) } return appConfig, nil }
使用變化功能旗標
使用下列內容建立名為
main.go的檔案:package main import ( "context" "fmt" "log" "net/http" "strings" "github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration" "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" "github.com/microsoft/Featuremanagement-Go/featuremanagement" "github.com/microsoft/Featuremanagement-Go/featuremanagement/providers/azappconfig" ) type Quote struct { Message string `json:"message"` Author string `json:"author"` } type WebApp struct { featureManager *featuremanagement.FeatureManager appConfig *azureappconfiguration.AzureAppConfiguration quotes []Quote } func main() { // Load Azure App Configuration appConfig, err := loadAzureAppConfiguration(context.Background()) if err != nil { log.Fatalf("Error loading Azure App Configuration: %v", err) } // Create feature flag provider featureFlagProvider, err := azappconfig.NewFeatureFlagProvider(appConfig) if err != nil { log.Fatalf("Error creating feature flag provider: %v", err) } // Create feature manager featureManager, err := featuremanagement.NewFeatureManager(featureFlagProvider, nil) if err != nil { log.Fatalf("Error creating feature manager: %v", err) } // Initialize quotes quotes := []Quote{ { Message: "You cannot change what you are, only what you do.", Author: "Philip Pullman", }, } // Create web app app := &WebApp{ featureManager: featureManager, appConfig: appConfig, quotes: quotes, } // Setup Gin with default middleware (Logger and Recovery) r := gin.Default() // Start server if err := r.Run(":8080"); err != nil { log.Fatalf("Failed to start server: %v", err) } fmt.Println("Starting Quote of the Day server on http://localhost:8080") fmt.Println("Open http://localhost:8080 in your browser") fmt.Println() }使用中介軟體,從 Azure 應用程式組態啟用設定和功能旗標重新整理。
// Existing code // ... ... func (app *WebApp) refreshMiddleware() gin.HandlerFunc { return func(c *gin.Context) { go func() { if err := app.appConfig.Refresh(context.Background()); err != nil { log.Printf("Error refreshing configuration: %v", err) } }() c.Next() } } // The rest of existing code //... ...使用下列內容設定路由:
// Existing code // ... ... func (app *WebApp) setupRoutes(r *gin.Engine) { // Setup sessions store := cookie.NewStore([]byte("secret-key-change-in-production")) store.Options(sessions.Options{ MaxAge: 3600, // 1 hour HttpOnly: true, Secure: false, // Set to true in production with HTTPS }) r.Use(sessions.Sessions("session", store)) r.Use(app.refreshMiddleware()) // Load HTML templates r.LoadHTMLGlob("templates/*.html") // Routes r.GET("/", app.homeHandler) r.GET("/login", app.loginPageHandler) r.POST("/login", app.loginHandler) r.GET("/logout", app.logoutHandler) } // Home page handler func (app *WebApp) homeHandler(c *gin.Context) { session := sessions.Default(c) username := session.Get("username") quote := app.quotes[0] var greetingMessage string var targetingContext featuremanagement.TargetingContext if username != nil { // Create targeting context for the user targetingContext = createTargetingContext(username.(string)) // Get the Greeting variant for the current user if variant, err := app.featureManager.GetVariant("Greeting", targetingContext); err != nil { log.Printf("Error getting Greeting variant: %v", err) } else if variant != nil && variant.ConfigurationValue != nil { // Extract the greeting message from the variant configuration if configValue, ok := variant.ConfigurationValue.(string); ok { greetingMessage = configValue } } } c.HTML(http.StatusOK, "index.html", gin.H{ "title": "Quote of the Day", "user": username, "greetingMessage": greetingMessage, "quote": quote, }) } func (app *WebApp) loginPageHandler(c *gin.Context) { c.HTML(http.StatusOK, "login.html", gin.H{ "title": "Login - Quote of the Day", }) } func (app *WebApp) loginHandler(c *gin.Context) { email := strings.TrimSpace(c.PostForm("email")) // Basic validation if email == "" { c.HTML(http.StatusOK, "login.html", gin.H{ "title": "Login - Quote of the Day", "error": "Email cannot be empty", }) return } if !strings.Contains(email, "@") { c.HTML(http.StatusOK, "login.html", gin.H{ "title": "Login - Quote of the Day", "error": "Please enter a valid email address", }) return } // Store email in session session := sessions.Default(c) session.Set("username", email) if err := session.Save(); err != nil { log.Printf("Error saving session: %v", err) } c.Redirect(http.StatusFound, "/") } func (app *WebApp) logoutHandler(c *gin.Context) { session := sessions.Default(c) session.Clear() if err := session.Save(); err != nil { log.Printf("Error saving session: %v", err) } c.Redirect(http.StatusFound, "/") } // Helper function to create TargetingContext func createTargetingContext(userID string) featuremanagement.TargetingContext { targetingContext := featuremanagement.TargetingContext{ UserID: userID, Groups: []string{}, } if strings.Contains(userID, "@") { parts := strings.Split(userID, "@") if len(parts) == 2 { domain := parts[1] targetingContext.Groups = append(targetingContext.Groups, domain) // Add domain as group } } return targetingContext } // The rest of existing code //... ...使用下列內容更新
main.go:// Existing code // ... ... r := gin.Default() // Setup routes app.setupRoutes(r) // Start server if err := r.Run(":8080"); err != nil { log.Fatalf("Failed to start server: %v", err) } // The rest of existing code // ... ...
建置並執行應用程式
設定用於驗證的環境變數 ,並執行應用程式:
go mod tidy go run .開啟瀏覽器並巡覽至
http://localhost:8080。 選取右上方的 [登入] 以 usera@contoso.com 身分登入。
登入後,您會看到給 usera@contoso.com 的一則長問候訊息。
按一下 [登出] 並以 userb@contoso.com 身分登入,您會看到簡單問候訊息。
備註
請務必讓本教學課程正確使用這些名稱。 只要功能如預期般設定,這兩個使用者應該會看到不同的變體。
後續步驟
若要深入了解 Go 中的功能管理,請繼續閱讀下列文件: