Middleware
ASP.NET Core middleware
Install the ASP.NET Core companion package:
dotnet add package Kinetq.LiquidPages.AspNetCore
Register LiquidPages services, initialize startup registrations, and map LiquidPages endpoints:
using Kinetq.LiquidPages.AspNetCore;
using Kinetq.LiquidPages.Helpers;
using Kinetq.LiquidPages.Interfaces;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddLiquidPages(typeof(Program).Assembly);
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var startup = scope.ServiceProvider.GetRequiredService<ILiquidStartup>();
await startup.RegisterPageModels();
await startup.RegisterFilters();
string workingDirectory = Directory.GetCurrentDirectory();
startup.RegisterFileProvider("/", new PhysicalFileProvider(workingDirectory));
}
app.UseLiquidPagesErrorHandling();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapLiquidPages();
});
await app.RunAsync();
GenHTTP middleware
Install the GenHTTP companion package:
dotnet add package Kinetq.LiquidPages.GenHTTP
Resolve ILiquidResponseMiddleware and ILiquidRoutesManager from your container and pass both to LiquidHandlerBuilder:
var middleware = serviceProvider.GetRequiredService<ILiquidResponseMiddleware>();
var routesManager = serviceProvider.GetRequiredService<ILiquidRoutesManager>();
await Host.Create()
.Handler(new LiquidHandlerBuilder(middleware, routesManager))
.Bind(IPAddress.Any, 8080)
.RunAsync();
LiquidHandlerBuilder implements IHandlerBuilder<LiquidHandlerBuilder>, so you can attach any GenHTTP concern (compression, caching, CORS, etc.) before the handler is built. See the full GenHTTP documentation for a complete walkthrough.
EmbedIO middleware
Install the EmbedIO companion package and attach LiquidPages to your WebServer:
dotnet add package Kinetq.LiquidPages.EmbedIO
var middleware = serviceProvider.GetRequiredService<ILiquidResponseMiddleware>();
var routesManager = serviceProvider.GetRequiredService<ILiquidRoutesManager>();
webServer.WithLiquidPages(middleware, routesManager);
If you need lower-level control, LiquidWebModule now takes ILiquidRoutesManager in its constructor:
webServer.WithModule(new LiquidWebModule("/", routesManager)
{
LiquidResponseMiddleware = middleware
});
Custom Middleware
Otherwise if there is no existing middleware for your webserver, the implementation would still be simple and look something like this:
try
{
var liquidRequest = new LiquidRequestModel()
{
Route = request.Url.AbsolutePath,
QueryParams = request.Url.Query.GetQueryParams(),
Headers = request.Headers
};
if (request.HasEntityBody)
{
using var reader = new StreamReader(request.InputStream, Encoding.UTF8);
liquidRequest.Body = await reader.ReadToEndAsync();
}
var responseModel =
await LiquidResponseMiddleware.HandleRequestAsync(liquidRequest);
response.ContentLength64 = responseModel.Content.Length;
response.ContentType = responseModel.ContentType;
response.StatusCode = responseModel.StatusCode;
await response.OutputStream.WriteAsync(responseModel.Content);
}
catch (Exception ex)
{
response.StatusCode = 500;
byte[] errorBuffer = Encoding.UTF8.GetBytes($"Internal Server Error: {ex.Message}");
response.ContentLength64 = errorBuffer.Length;
response.ContentType = "text/html";
await response.OutputStream.WriteAsync(errorBuffer);
}
finally
{
response.Close();
}
}
Liquid Response Middleware
The ILiquidResponseMiddleware is the engine that ties everything together. After injecting it into your application, you call HandleRequestAsync with a LiquidRequestModel that encapsulates the incoming request. The middleware then orchestrates route matching, data retrieval, template parsing, and response generation, returning a LiquidResponseModel ready to be written to the output stream.
ILiquidResponseMiddlewareis the core service; itsHandleRequestAsyncmethod accepts aLiquidRequestModelcontaining the route, query parameters, body, and headers.The middleware iterates through all registered routes, matching the request path against each
RoutePattern(regex).When a match is found, it optionally invokes the route’s
Executedelegate to obtain a view model.The middleware then parses the Liquid template using the Fluid engine, passing the view model (accessible as
view_model) and any custom filters registered in the system.If no route matches, it attempts to serve static files (e.g., CSS, images) from the default file provider.
If neither a route nor a static file is found, it falls back to any configured error routes (such as the 404 route described above) to produce an appropriate response.
The final output is a byte array with the correct MIME type and HTTP status code, packaged in a
LiquidResponseModel.This design makes the middleware adaptable to any web server, as demonstrated in the generic integration example at the beginning of this document. For a detailed look at the implementation, see the
LiquidResponseMiddleware.csfile in the repository.
