Skip to content

Middleware Pipeline backed by OptionsMonitor to be reloaded at runtime. #26564

@dazinator

Description

@dazinator

Is your feature request related to a problem? Please describe.

When configuring the middleware pipeline on application startup, there are a lot of things that can get built, based on the application configuration at that moment in time. Here is a contrived example, real application middleware pipelines are obviously much more complex:

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                //app.UseWebAssemblyDebugging();
            }
            else
            {

                app.UseExceptionHandler("/Error");

                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                if (Configuration["UseHsts"] == "True")
                {
                    app.UseHsts();
                }
            }
  1. Once the RequestDelegate has been built, there is no opportunity to respond to any configuration change and re-build it at runtime. For example, if the configuration value for UseHsts was changed shown above, it would be good for the change in this configuration to cause a new RequestDelegate to be built based on latest configuration.

  2. It would be nice imho to be able to use / leverage the options pattern, so one can have an options instance passed in when configuring / building the middleware pipeline. Note in the case above, the mixed access to various sources that impact the way the pipeline is built, like: Configuration["UseHsts"] but also env.IsDevelopment() plus customers might also bind options instances directly to various config sections etc - it would be perhaps easier to reason about more complex pipelines if values influencing the control flow of the pipeline could be consolidated into one TOptions object like this:

    public class PipelineOptions
    {
        public bool UseDeveloperExceptionPage { get; set; }
        public bool UseHsts { get; set; }
    }

 

Describe the solution you'd like

Here are a couple of very loose suggestions, for how this feature might look, but I don't want to come across as prescriptive in terms of the API's here..

  1. I'd like to be able to use a TOptions instance when building the middleware pipeline.
  2. I'd like for that to happen automatically whenever TOptions changes at runtime.

This means I'd probably need some way of registering this somewhere - perhaps in ConfigureServices:

        public void ConfigureServices(IServiceCollection services)
        {
            // Registers an action that can be used to configure a pipeline that will be called whenever TOptions changes.
            services.ConfigureRequestDelegate<PipelineOptions>(Configuration, Configure); // registring this is pointless by itself, something would have to monitor for changes and use this factory etc.
        }


        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, PipelineOptions options)
        {
            // Note: PipelineOptions has been passed in reflecting up to date / current options from which I can build the pipeline.
            if (options.UseDeveloperExceptionPage)
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                if (options.UseHsts) // I get to use my options class everywhere important. Much cleaner in large pipelines that use a lot of config.
                {
                    app.UseHsts();
                }
            }  
        }

    public class PipelineOptions
    {
        public bool UseDeveloperExceptionPage { get; set; }
        public bool UseHsts { get; set; }
    }

To make this less verbose, its possible the Hosting layer could add something smart for detecting Configure() methods that should be backed by Options Manager / Monitor (I always forget which one of those is the one!) by detecting a variant of a Configure() method with a signature like this indicating a TOptions type:


        /// This method would be called at application startup but also whenever `PipelineOptions` changes.
        public void Configure<PipelineOptions>(IApplicationBuilder app, IWebHostEnvironment env, PipelineOptions options)
        {
            // Note: PipelineOptions has been passed in reflecting up to date / current options from which I can build the pipeline.
            if (options.UseDeveloperExceptionPage)
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                if (options.UseHsts) // I get to use my options class everywhere important. Much cleaner in large pipelines that use a lot of config.
                {
                    app.UseHsts();
                }
            }  
        }

If that method was detected, then by convention it could configure the TOptions instance (perhaps to some default config section prefix like "PipelineOptions:"?) and then register a factory service to provide RequestDelegate at runtime which would be backed by OptionsMonitor to return current version of RequestDelegate and rebuld it when changes occur.

Additional context

Add any other context or screenshots about the feature request here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    affected-very-fewThis issue impacts very few customersarea-hostingIncludes Hostingarea-networkingIncludes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractionsenhancementThis issue represents an ask for new feature or an enhancement to an existing onemulti-tenancyIssues based in multi-tenancyseverity-nice-to-haveThis label is used by an internal tool

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions