Services & Middleware
Extending services and middleware with the IPipeline interface
With .NET Core, Microsoft introduced new conventions for registering services for dependency injection and configuring middleware. Dynamicweb 10 fully supports these conventions using the IPipeline interface.
Note
- Use
IPipelineto register services with ASP.NET Core's dependency injection. - Use
IPipelineto configure middleware in the Dynamicweb 10 application.
How IPipeline Maps to Program.cs
In a typical ASP.NET Core application, developers customize the startup process in Program.cs:
- Service registration happens via
builder.Services.Add... - Middleware configuration happens via
app.Use...
In Dynamicweb 10, you normally don’t have direct access to Program.cs since the platform is cloud-hosted and managed. Instead, Dynamicweb introduces the IPipeline interface as the extension point.
Under the hood, Dynamicweb’s Program.cs is very minimal:
var app = WebApplication
.CreateBuilder(args)
.SetupDynamicweb();
app.Run();
The heavy lifting is done inside DynamicwebApplicationExtensions (SetupDynamicweb, AddDynamicweb, UseDynamicweb).
These methods load services, middleware, and importantly — discover and run all IPipeline implementations registered as add-ins.
Program.cs vs IPipeline
| Concept in Program.cs | IPipeline Equivalent in Dynamicweb |
|---|---|
builder.Services.Add... (DI registration) |
RegisterServices(IServiceCollection services, IMvcCoreBuilder mvcBuilder) |
app.Use... (middleware setup) |
RegisterApplicationComponents(IApplicationBuilder app) |
| Startup logic (e.g., cache warm-up) | RunInitializers() |
| Execution order of middleware | Controlled by IPipeline.Rank property |
How Dynamicweb Boots Pipelines
When the Dynamicweb host starts:
AddDynamicweb loads add-ins and discovers all
IPipelineimplementations. These are resolved viaAddInManagerand added to DI.Each pipeline’s
RegisterServicesis executed to register dependencies. If one fails, it is marked as a “broken pipe” and skipped later.After building the app, UseDynamicweb runs and calls
UsePipelines. This executesRegisterApplicationComponentsin rank order to wire up middleware.Finally,
RunInitializersis invoked for any startup logic (e.g., seeding caches).
This makes IPipeline the Dynamicweb abstraction over the standard Program.cs, providing a structured and safe way for developers to extend services and middleware without direct access to the host configuration.
Background
For detailed information on services and middleware in ASP.NET Core, refer to these official Microsoft resources:
Getting Started
To extend Dynamicweb services or middleware:
- Reference the
Dynamicweb.Host.CoreNuGet package in your project. - Create a class implementing the
IPipelineinterface. - Implement necessary methods (
RegisterServices,RegisterApplicationComponents,RunInitializers). - Register your extension as an add-in within the Dynamicweb administration.
Dynamicweb automatically loads your implementation at startup.
Implementation Example
Here's a basic example of using the IPipeline:
Note
- Pipeline registration runs on application start
- Adding new IPipeline implementation to a Dynamicweb solution requires an application restart (recycle)
using Dynamicweb.Host.Core;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace CustomCode;
public class PipelineExtensions : IPipeline
{
public int Rank => 101;
public void RegisterApplicationComponents(IApplicationBuilder app)
{
app.UseMiddleware<SomeMiddleware>();
}
public void RegisterServices(IServiceCollection services, IMvcCoreBuilder mvcBuilder)
{
services.AddTransient<SomeService>();
services.AddHttpClient<SomeHttpService>(options =>
options.BaseAddress = new Uri("https://www.dynamicweb.dk"));
}
public void RunInitializers()
{
var service = ServiceLocator.Current.GetInstance<SomeService>();
service.Initialize();
}
}
Implementation Example - Custom Web API
You can create custom web API endpoints in your Dynamicweb project by registering controllers and routing paths:
Note
- Pipeline registration runs on application start
- Adding new IPipeline implementation to a Dynamicweb solution requires an application restart (recycle)
Understanding IPipeline and IWebApi
Dynamicweb’s hosting model uses pipelines to bootstrap features during application startup. A pipeline is simply a class that implements IPipeline, giving you three extension points:
- RegisterServices – lets you add services, controller assemblies, MVC configuration, or anything needed by the DI container.
- RegisterApplicationComponents – runs during application startup and allows you to register middleware, routing rules, or other components on the ASP.NET Core pipeline.
- RunInitializers – used for optional startup tasks that should execute after the application is ready.
Pipelines are discovered automatically through the Add-In system (AddInManager) and are executed in order of their Rank property. Lower ranks run earlier.
What IWebApi Does
IWebApi is a marker interface—it doesn’t contain any methods.
When a pipeline implements IWebApi, Dynamicweb treats its assembly as a provider of Web API controllers.
During startup, Dynamicweb:
- Locates all types implementing
IWebApi. - Collects their assemblies.
- Adds those assemblies as Application Parts to MVC so your controllers become discoverable.
- Configures internal Web API middleware (
UseDynamicwebWebApi) so the routes inside these assemblies are activated. - Generates documentation for the controller-defined endpoints which is made available through the Swagger UI (
/dwapi/docs)
This means that simply implementing IWebApi in your pipeline is enough for Dynamicweb to automatically load and activate your custom API controllers.
Note
Be aware that your controller-defined endpoints become discoverable through the DynamicWeb Delivery API documentation explorer when using IWebApi.
See below for further information if discoverability of endpoints is not desired.
How Pipelines Are Loaded
Dynamicweb initializes pipelines in two phases:
Service Registration Phase
When the application builds its service container,
AddPipelines:- Locates all
IPipelineimplementations viaAddInManager. - Instantiates them.
- Registers them as singletons.
- Calls
RegisterServiceson each pipeline so they can add services, MVC configuration, or controller assemblies. - Tracks any pipelines that throw exceptions and prevents them from running later.
- Locates all
Application Startup Phase
When the middleware pipeline is built,
UsePipelines:- Orders pipelines by
Rank. - Calls
RegisterApplicationComponentsfor each one. - Skips any that previously failed in the service registration phase.
- Orders pipelines by
If a pipeline implements both IPipeline and IWebApi, Dynamicweb will:
- Add its assembly as an MVC Application Part
- Register Dynamicweb’s Web API middleware
- Activate its controllers automatically
Restarting the application is required when you add a new pipeline class so it can be discovered by the Add-In system.
You should not implement IWebApi when:
- Discoverability of controller-defined endpoints through Swagger UI is not desired
- Not using controllers at all
Using only Minimal APIs or middleware, implementing
IWebApihas no effect
If you want to use controller-defined endpoints but want to avoid making them discoverable, do not implement the interface IWebApi. You must instead call mvcBuilder.AddApplicationPart in RegisterServices to add the assembly in which your controller resides. Otherwise the endpoints are not found and served.
Step 1: Registering Controllers and Routing
using Dynamicweb.Host.Core;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
public class CustomApiPipeline : IPipeline, IWebApi
{
public int Rank => 200;
public void RegisterApplicationComponents(IApplicationBuilder app)
{
app.MapWhen(IsCustomApiPath, apiApp =>
{
apiApp.UseRouting();
apiApp.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
});
static bool IsCustomApiPath(HttpContext context) =>
context.Request.Path.StartsWithSegments("/myapi", StringComparison.OrdinalIgnoreCase);
}
public void RegisterServices(IServiceCollection services, IMvcCoreBuilder mvcBuilder)
{
// Optional registering of services here
}
public void RunInitializers()
{
// Optional initialization logic here
}
}
Use this snippet instead if you don't implement IWebApi in your pipeline:
public void RegisterServices(IServiceCollection services, IMvcCoreBuilder mvcBuilder)
{
mvcBuilder.AddApplicationPart(typeof(MyApiController).Assembly);
}
Step 2: Implementing the Controller
Create your API controller class:
using Microsoft.AspNetCore.Mvc;
namespace CustomCode.Controllers;
[ApiController]
[Route("myapi/test")]
public class MyApiController : ControllerBase
{
public class GreetingResponse
{
public string? Message { get; set; }
}
[HttpGet("hi")]
public IActionResult Hi([FromQuery] string name = "World")
{
return Ok(new GreetingResponse
{
Message = $"Hello {name}!"
});
}
}
This implementation registers the controller under /myapi/test. Accessing /myapi/test/hi?name=Dynamicweb returns {"Message": "Hello Dynamicweb!"}.
Understanding IPipeline Concepts
IPipeline.Rank
Middleware execution order matters. Use the Rank property to define execution sequence. Middleware is executed in ascending order by rank. Dynamicweb built-in middleware typically uses ranks 1–100. Set your rank above 100 to avoid conflicts.
IPipeline.RegisterServices
Register services needed by your application using IServiceCollection. Common uses include configuring HttpClient instances. Leave empty if no additional services are required.
IPipeline.RegisterApplicationComponents
Configure middleware or routing using the ASP.NET IApplicationBuilder. Use carefully, as misconfigurations can severely impact your application. Leave empty if not required.
IPipeline.RunInitializers
Use this method to execute startup logic, such as populating caches or initializing stateful services. Leave empty if not needed.
Common Use Cases
Most developers will touch IPipeline when they need to extend Dynamicweb beyond the out-of-the-box features. A few typical examples include registering services, like adding a custom HttpClient for calling an external API, or introducing a caching layer with IMemoryCache. Another common case is adding middleware, for example logging every incoming request or injecting custom headers. You can also use pipelines to expose APIs, such as registering controllers to handle /myapi/* routes. These scenarios map directly to what you would normally configure in Program.cs of a standard ASP.NET Core app, but in Dynamicweb they are handled through IPipeline implementations — giving developers a safe and modular way to drop in new behavior without touching the platform’s host code.
Pitfalls to Avoid
While pipelines make extension easier, there are a few pitfalls to watch for. The most common issue is service lifetime mismatches: registering scoped services and then consuming them in singletons can create subtle bugs or memory leaks. Another risk is pipeline ordering conflicts: if multiple add-ins register middleware at the same Rank, the execution order might not be what you expect.
Middleware configuration is especially sensitive. For example, registering middleware that intercepts /home could override Dynamicweb’s built-in PageViewMiddleware. This would cause the /home page (a normal Dynamicweb URL) to stop working entirely, since your pipeline would hijack the request before Dynamicweb could handle it.
public class BadPipeline : IPipeline
{
public int Rank => 150;
public void RegisterApplicationComponents(IApplicationBuilder app)
{
// 🚨 This will "steal" /home requests before Dynamicweb can handle them
app.MapWhen(ctx => ctx.Request.Path.StartsWithSegments("/home"), homeApp =>
{
homeApp.Run(async context =>
{
await context.Response.WriteAsync("Hello from my custom /home!");
});
});
}
public void RegisterServices(IServiceCollection services, IMvcCoreBuilder mvcBuilder) { }
public void RunInitializers() { }
}
In this example, the /home path is taken over by the custom pipeline. Dynamicweb’s page rendering logic never runs, effectively breaking the CMS frontend.
Overriding Dynamicweb Behavior Intentionally
That same mechanism, however, can be used deliberately. For instance, you might want to override Dynamicweb’s default /sitemap.xml generation with your own logic. This is a valid case where “hijacking” the request is useful:
public class CustomSitemapPipeline : IPipeline
{
public int Rank => 160;
public void RegisterApplicationComponents(IApplicationBuilder app)
{
app.MapWhen(ctx => ctx.Request.Path.Equals("/sitemap.xml", StringComparison.OrdinalIgnoreCase), sitemapApp =>
{
sitemapApp.Run(async context =>
{
context.Response.ContentType = "application/xml";
await context.Response.WriteAsync("<urlset><url><loc>https://example.com/custom</loc></url></urlset>");
});
});
}
public void RegisterServices(IServiceCollection services, IMvcCoreBuilder mvcBuilder) { }
public void RunInitializers() { }
}
Here, /sitemap.xml requests are intentionally intercepted and replaced with custom XML. Instead of breaking the system, this approach provides a clean way to override the default Dynamicweb output and adapt it to project-specific requirements.
The takeaway: Middleware hijacking can either accidentally block Dynamicweb features (bad) or intentionally override them (good). The difference lies in whether the interception is deliberate and well-tested.