File: Startup.cs
Web Access
Project: src\src\Middleware\HeaderPropagation\samples\HeaderPropagationSample\HeaderPropagationSample.csproj (HeaderPropagationSample)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Net.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Primitives;
 
namespace HeaderPropagationSample;
 
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRouting();
 
        // A sample for configuration for propagating an A/B testing header. If we see
        // the X-BetaFeatures header then we forward it to outgoing calls. If we don't
        // see it, then we generate a new value randomly.
        //
        // To see this in action, send a request to /
        // - If you do not specify X-BetaFeatures the server will generate a new value
        // - If you specify X-BetaFeatures then you will see the value propagated
        //
        // This demonstrates two common uses of header propagation:
        // 1. Forward a header as-is
        // 2. Generate a new header value, or conditionally generate a header value
        //
        // It's also easy to forward a header with a different name, using Add(string, string)
        services.AddHeaderPropagation(options =>
        {
            // Propagate the X-BetaFeatures if present.
            options.Headers.Add("X-BetaFeatures");
 
            // Generate a new X-BetaFeatures if not present.
            options.Headers.Add("X-BetaFeatures", context =>
            {
                return GenerateBetaFeatureOptions();
            });
        });
 
        services
            .AddHttpClient("test")
            .AddHeaderPropagation();
 
        services
            .AddHttpClient("another")
            .AddHeaderPropagation(options => options.Headers.Add("X-BetaFeatures", "X-Experiments"));
    }
 
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHttpClientFactory clientFactory)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
 
        app.UseHeaderPropagation();
 
        app.UseRouting();
 
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGet("/", async context =>
            {
                foreach (var header in context.Request.Headers)
                {
                    await context.Response.WriteAsync($"'/' Got Header '{header.Key}': {string.Join(", ", (string[])header.Value)}\r\n");
                }
 
                var clientNames = new[] { "test", "another" };
                foreach (var clientName in clientNames)
                {
                    await context.Response.WriteAsync("Sending request to /forwarded\r\n");
 
                    var uri = UriHelper.BuildAbsolute(context.Request.Scheme, context.Request.Host, context.Request.PathBase, "/forwarded");
                    var client = clientFactory.CreateClient(clientName);
                    var response = await client.GetAsync(uri);
 
                    foreach (var header in response.RequestMessage.Headers)
                    {
                        await context.Response.WriteAsync($"Sent Header '{header.Key}': {string.Join(", ", header.Value)}\r\n");
                    }
 
                    await context.Response.WriteAsync("Got response\r\n");
                    await context.Response.WriteAsync(await response.Content.ReadAsStringAsync());
                }
            });
 
            endpoints.MapGet("/forwarded", async context =>
            {
                foreach (var header in context.Request.Headers)
                {
                    await context.Response.WriteAsync($"'/forwarded' Got Header '{header.Key}': {string.Join(", ", (string[])header.Value)}\r\n");
                }
            });
        });
    }
 
    private static StringValues GenerateBetaFeatureOptions()
    {
        var features = new string[]
        {
                "Widgets",
                "Social",
                "Speedy-Checkout",
        };
 
        var threshold = 0.80; // 20% chance for each feature in beta.
 
        var values = new List<string>();
        for (var i = 0; i < features.Length; i++)
        {
            if (Random.Shared.NextDouble() > threshold)
            {
                values.Add(features[i]);
            }
        }
 
        if (values.Count == 0)
        {
            return new StringValues("none");
        }
 
        return new StringValues(values.ToArray());
    }
}