Developer forum

Forum » Development » How to register a custom middleware before Dynamicweb built-in middlewares?

How to register a custom middleware before Dynamicweb built-in middlewares?

Ruwan Dissanayake
Reply

Hi everyone,

I would like to register a custom middleware that intercepts user requests before Dynamicweb components handle their routing.

According to the https://doc.dynamicweb.dev/documentation/extending/middleware/index.html#ipipelinerank, Ranks 1 to 100 are reserved for Dynamicweb's built-in middlewares.

 

I have a couple of questions regarding this:

  1. How can I register a custom middleware so that it executes before the built-in Dynamicweb middlewares are registered to the pipeline?

  2. Can I use Microsoft.AspNetCore.Hosting.IStartupFilter to register my custom middleware and insert it at the very beginning of the pipeline?

Thanks in advance for your help!
 


Replies

 
Ruwan Dissanayake
Reply

Please find the below implementation. I deployed this code to staging, but it seems my GeoRoutingMiddleware is not registered in staging environment. 

    public class GeoRoutingPipeline : IPipeline
    {
        // Ranks 1-100 are reserved for Dynamicweb. 101 ensures it runs early among custom pipelines.
        public int Rank => 101;

        public void RegisterServices(IServiceCollection services, IMvcCoreBuilder mvcBuilder)
        {
            services.AddSingleton<GeoLiteCountryDatabase>(provider =>
            {
                // In Dynamicweb Cloud / staging environments, map the path from /Files
                var filesFolder = Dynamicweb.Core.SystemInformation.MapPath("/Files");
                var dbPath = Path.GetFullPath(Path.Combine(filesFolder, "Files", "Integration", "GeoIP", "GeoLite2-Country.mmdb"));

                if (!File.Exists(dbPath))
                {
                    // Fallback to local development path if needed, though MapPath("/Files") works locally too usually
                    var env = provider.GetRequiredService<Microsoft.AspNetCore.Hosting.IWebHostEnvironment>();
                    dbPath = Path.GetFullPath(Path.Combine(env.ContentRootPath, "..", "SampleProject.Files", "Files", "Files", "Integration", "GeoIP", "GeoLite2-Country.mmdb"));
                }

                if (!File.Exists(dbPath))
                    throw new FileNotFoundException($"GeoIP database not found at: {dbPath}");

                var db = new GeoLiteCountryDatabase(dbPath);
                GeoLiteCountryDatabase.SetInstance(db);
                return db;
            });

            // Register an IStartupFilter to ensure the middleware is inserted at the very beginning of the pipeline
            services.AddTransient<Microsoft.AspNetCore.Hosting.IStartupFilter, GeoRoutingStartupFilter>();
        }

        public void RegisterApplicationComponents(IApplicationBuilder app)
        {
            // Middleware is now registered via IStartupFilter in RegisterServices
            // to ensure it runs before Dynamicweb's internal routing endpoints.
        }

        public void RunInitializers()
        {
        }
    }

    public class GeoRoutingStartupFilter : Microsoft.AspNetCore.Hosting.IStartupFilter
    {
        public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
        {
            return builder =>
            {
                // Insert Custom Market Routing Middleware at the very top of the pipeline
                builder.UseMiddleware<GeoRoutingMiddleware>();
                next(builder);
            };
        }
    }
}

 

 

 
Nicolai Pedersen Dynamicweb Employee
Nicolai Pedersen
Reply

Hi Ruwan,

A couple of things to untangle here, because the two approaches you've tried (an IPipeline and an IStartupFilter) behave quite differently in Dynamicweb 10 — and the IStartupFilter one is almost certainly why nothing runs on staging.

Why the IStartupFilter doesn't fire

Dynamicweb 10 does not build its request pipeline through the conventional Startup.Configure convention. It is built explicitly inside app.UseDynamicweb() (called from WebApplicationBuilder.SetupDynamicweb()). IStartupFilter instances only run if two things are true:

  1. They are registered in DI yourself, e.g. services.AddTransient<IStartupFilter, GeoRoutingStartupFilter>(), and
  2. The host actually invokes the Configure filter chain.

If you registered the filter from inside your IPipeline.RegisterServices, the ordering/timing won't reliably wrap Dynamicweb's pipeline, and if you registered it from a separate Program.cs that differs between your local run and staging, it simply won't be there on staging. Either way, IStartupFilter is the wrong tool here — drop it.

The supported way: just use your IPipeline

You already have the right vehicle. IPipeline.RegisterApplicationComponents(IApplicationBuilder app) is exactly where you call app.UseMiddleware<GeoRoutingMiddleware>() — no IStartupFilter needed:

public sealed class GeoRoutingPipeline : IPipeline

{

    // Lower rank = earlier. See note below about ranks 1–100.

    public int Rank => 101;

 

    public void RegisterServices(IServiceCollection services, IMvcCoreBuilder mvcBuilder)

    {

        services.AddSingleton<GeoIpDatabase>(); // your GeoIP service

    }

 

    public void RegisterApplicationComponents(IApplicationBuilder app)

    {

        app.UseMiddleware<GeoRoutingMiddleware>();

    }

 

    public void RunInitializers() { }

}

How Dynamicweb wires this up (from PipelineExtensions):

  • Pipelines are discovered through the AddIn manager (AddInManager.GetTypes<IPipeline>()), instantiated with AddInManager.GetInstance<IPipeline>(), then ordered by Rank and run via app.UsePipelines().
  • All IPipeline.RegisterApplicationComponents calls run together, ordered by Rank, near the end of UseDynamicweb().

About "before the built-in middlewares" and ranks 1–100

This is the part worth being precise about. The rank only orders the pipeline registrations relative to each other inside UsePipelines(). The classic frontend (routing/URL handling/NotFound etc.) lives in ClassicFrontendPipeline with Rank = int.MaxValue, so any custom pipeline with a lower rank registers its middleware before the frontend's — which is what you want for geo-routing/redirect logic.

However, a number of framework middlewares (forwarded headers, restricted access, compression, file providers, extensibility, installation/license, rate limiter) are registered directly in UseDynamicweb() before UsePipelines() is ever called. You cannot get in front of those via IPipeline regardless of rank — and you generally don't want to (you'd be ahead of forwarded-headers handling, which would break the very client-IP detection a GeoIP middleware depends on).

So: use Rank => 101 (rank 101+ is fine for custom code; 1–100 is reserved), put UseMiddleware in RegisterApplicationComponents, and your middleware will sit ahead of the frontend routing while still benefiting from forwarded headers being resolved first.

Why it works locally but not on staging

Because discovery goes through the AddIn manager, the most common reason a pipeline runs locally but not on staging is that the assembly isn't being loaded on staging. Check:

  1. Your DLL is actually deployed to the AddIns location that's scanned — /Files/System/AddIns/Installed/<yourfolder>/ — and the folder isn't blacklisted.
  2. The class is public and has a public parameterless constructor (the AddIn manager instantiates it).
  3. The startup log. Each discovered pipeline logs Running pipeline: '<FullName>'. If you don't see GeoRoutingPipeline there on staging, it was never discovered (deployment/assembly issue). If you do see it but get a registration error, RegisterServices threw — failures there are caught and logged as RegisterServices threw an exception, and the pipeline is then skipped (added to "broken pipes"), so its middleware won't run.

Confirm the staging startup log first — that single line tells you whether this is a discovery problem (DLL not deployed) or a registration problem (exception in RegisterServices). My guess given "works locally, not on staging" is the former: the build with your middleware assembly didn't make it into the staging deployment's AddIns folder.

Best regards

 

You must be logged in to post in the forum