Elsie.HealthChecks 0.2.0-alpha.2

Suggested Alternatives

Elsie

This is a prerelease version of Elsie.HealthChecks.
The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package Elsie.HealthChecks --version 0.2.0-alpha.2
                    
NuGet\Install-Package Elsie.HealthChecks -Version 0.2.0-alpha.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Elsie.HealthChecks" Version="0.2.0-alpha.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Elsie.HealthChecks" Version="0.2.0-alpha.2" />
                    
Directory.Packages.props
<PackageReference Include="Elsie.HealthChecks" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Elsie.HealthChecks --version 0.2.0-alpha.2
                    
#r "nuget: Elsie.HealthChecks, 0.2.0-alpha.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Elsie.HealthChecks@0.2.0-alpha.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Elsie.HealthChecks&version=0.2.0-alpha.2&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Elsie.HealthChecks&version=0.2.0-alpha.2&prerelease
                    
Install as a Cake Tool

Elsie

HTTP module framework for .NET 8 and .NET 10. Define routes in small modules, return results, host on ASP.NET Core.

dotnet add package Elsie          # 0.2.0-alpha.2 — pulls Elsie.AspNetCore + Elsie.Core
using Elsie;
using Elsie.AspNetCore;

ElsieWeb.Run<App>(args);

public sealed class App : ElsieModule
{
    public App()
    {
        Get("/", () => ElsieResult.Text("Hello, Elsie!"));
        Get("/hello/{name}", ctx =>
            ElsieResult.Text($"Hello {ctx.RouteOrDefault("name")}!"));
    }
}
dotnet run
# GET /           → Hello, Elsie!
# GET /hello/Ada  → Hello Ada!

Templates:

dotnet new install Elsie.Templates
dotnet new elsie -n HelloApp        # minimal app
dotnet new elsie-api -n TodosApi    # CRUD + API key + OpenAPI

Guides: docs/ · Samples: HelloWorld · Hello · Api · Views · Full · NuGet: Elsie


Quickstart (builder)

var builder = WebApplication.CreateBuilder(args);
builder.AddElsie();                              // DI + quieter console logs
builder.Services.AddElsieModule<TodosModule>();  // or rely on entry-assembly scan

var app = builder.Build();
app.MapElsieOpenApi(o =>
{
    o.Info.Title = "My API";
    o.UiPath = "/scalar";                        // optional Scalar CDN UI
});
app.MapElsie();
app.Run();
API Use
ElsieWeb.Run<T>(args) Smallest host
builder.AddElsie() Wire DI (ScanEntryAssembly = true by default)
AddElsieModule<T>() Explicit module registration (prefer in tests)
app.MapElsie() Map Elsie into the pipeline (non-terminal by default)

Modules are singletons. Inject singleton-safe services in the ctor; resolve request-scoped services with ctx.GetRequiredService<T>() / ctx.Services.

JSON: ElsieResult.Json(...) uses framework defaults (ElsieJson.DefaultOptions); ctx.Json(...) uses app ElsieOptions.JsonSerializerOptions.


Modules and routing

public sealed class TodosModule : ElsieModule
{
    public TodosModule(ITodoStore store)
    {
        Path("/api");
        Before(ElsieAuth.RequireApiKey("dev-secret", onlyMutatingMethods: true));

        Group("/todos", () =>
        {
            Get("/", ctx => ctx.Json(store.List(ctx.QueryOrDefault("q"))))
                .Named("listTodos")
                .WithTags("todos");

            Get("/{id:guid}", ctx =>
            {
                if (!ctx.RequireRoute("id", out Guid id, out var error))
                    return error!;
                return ctx.Json(store.Get(id));
            }).Named("getTodo").Produces<Todo>();

            Post("/", async (ctx, ct) =>
            {
                var bind = await ctx.BindJsonAsync<CreateTodo>(ct);
                if (!bind.IsSuccess) return bind.Error!;
                var created = store.Add(bind.Value!.Title);
                return ElsieResult.Created(ctx.UrlFor("getTodo", new { id = created.Id }), created);
            }).Accepts<CreateTodo>().Produces<Todo>(201);
        });
    }
}

Route templates: {name}, optional {name?}, default {name=5}, constraints {id:int}, catch-all {*path} (last segment only).

Built-in constraints: int, long, guid, bool, alpha, datetime, decimal, double, minlength(n), maxlength(n), length(n|min,max), min(n), max(n), range(a,b), regex(...).

Matching: per segment, static > constrained > param > catch-all. Startup fails on unknown constraints, duplicate param names, bad catch-all placement, ambiguous routes, and duplicate route names. Wrong verb on a known path → 405 + Allow + problem+json. HEAD maps to GET when ElsieOptions.ImplicitHead is on (default).


Results, binding, context

ElsieResult.Text("ok")
ElsieResult.Html("<p>hi</p>")
ElsieResult.Json(payload, statusCode: 201)   // framework JSON defaults
ctx.Json(payload)                            // app JsonSerializerOptions
ElsieResult.Created("/items/1", body)
ElsieResult.Accepted()
ElsieResult.File(bytes, "application/pdf", downloadName: "a.pdf")
ElsieResult.Redirect("/elsewhere")           // 302; 307/308 helpers also
ElsieResult.NoContent()
ElsieResult.NotModified()
ElsieResult.Problem(400, "Bad Request", detail)
ElsieResult.ValidationProblem(errors)
ElsieResult.ServerSentEvents(async (w, ct) => await w.WriteEventAsync("tick", "1", ct))

var bind = await ctx.BindJsonAsync<CreateTodo>(ct);
var form = await ctx.BindFormAsync<LoginForm>(ct);
var query = ctx.BindQuery<SearchQuery>();
ctx.Route<int>("id");
ctx.Query<bool>("shout");
ctx.RequireRoute("id", out Guid id, out var error);

ctx.Request.Method / Path / Scheme / Host / PathBase / Protocol / RemoteIp
ctx.Request.GetHeader / GetQuery / GetCookie
ctx.UrlFor("getTodo", new { id })
ctx.Response.Headers["X-App"] = "1";
ctx.Response.SetCookie("sid", value, new ElsieCookieOptions { HttpOnly = true });
ctx.GetRequiredService<IMyService>();

Exception handling (first match wins):

builder.AddElsie(o =>
{
    o.MapException<KeyNotFoundException>((_, ex) => ElsieResult.NotFound(ex.Message));
    o.ExceptionHandler = (ctx, ex, ct) =>
        Task.FromResult(ElsieResult.Problem(500, "Server Error", ex.Message));
});
// MapException → module OnError → ExceptionHandler → rethrow
// After-hooks still run when the exception is mapped to a result

Need HttpContext? Host escape hatch (core stays free of it):

Get("/trace", ctx =>
    ctx.TryGetHttpContext(out var http)
        ? ElsieResult.Text(http!.TraceIdentifier)
        : ElsieResult.Text("no-host"));

Pipelines

App-wide or per-module before / after hooks. After-hooks may transform the result.

builder.Services.ConfigureElsiePipelines(p =>
{
    p.AddBefore((ctx, _) =>
    {
        ctx.Response.Headers["X-Request-Id"] = Guid.NewGuid().ToString("n");
        return Task.FromResult<ElsieResult?>(null); // null = continue
    });
    p.AddAfter((ctx, result) =>
    {
        ctx.Response.Headers["X-Status"] = result.StatusCode.ToString();
        return result;
    });
});
// Order: app.Before → module.Before → handler → module.After → app.After

Core gates (no ASP.NET auth stack):

Before(ElsieAuth.RequireApiKey("dev-secret"));                    // all methods (default)
Before(ElsieAuth.RequireApiKey("dev-secret", onlyMutatingMethods: true));
Before(ElsieAuth.RequireHeader("X-Tenant", "acme"));
Before(ElsieAuth.RequireBearer(token => token == "ok"));
Before(ElsieAuth.RequireCookie("session"));

Cookie/JWT policies → Elsie.Auth.


Optional packages

Auth — Elsie.Auth

builder.Services.AddElsieAuth(o => o.Cookie = c => c.Cookie.Name = "elsie-auth");
app.UseElsieAuth(); // before MapElsie

Before(ElsieAuthGates.RequireAuthenticated());
Before(ElsieAuthGates.RequireRole("admin"));
await ctx.SignInCookieAsync("ada", roles: ["user"]);
var user = ctx.GetUser();

CORS — Elsie.Cors

Elsie handles OPTIONS preflight itself (ASP.NET UseCors does not see those requests).

builder.Services.AddElsieCors(o => o.AddDefaultPolicy(p => p
    .AllowOrigins("http://localhost:5173")
    .AllowMethods("GET", "POST", "OPTIONS")
    .AllowHeaders("Content-Type")
    .AllowCredentials()));
app.UseElsieCors(); // before MapElsie
// optional: Get(...).WithCors("policy-name")

Health — Elsie.HealthChecks

builder.Services.AddElsieHealthChecks(o =>
{
    o.AddCheck("self", () => ElsieHealthCheckResult.Healthy(), ElsieHealthCheckTags.Live);
    o.AddCheck("db", ct => CheckDbAsync(ct), ElsieHealthCheckTags.Ready);
});
// GET /healthz | /healthz/live | /healthz/ready  (unhealthy → 503)

Rate limiting — Elsie.RateLimiting

Before(ElsieRateLimit.FixedWindow(100, TimeSpan.FromMinutes(1)));
Before(ElsieRateLimit.SlidingWindow(30, TimeSpan.FromSeconds(10),
    partitionKey: ctx => ctx.Request.GetHeader("X-Api-Key") ?? "anon"));
// 429 problem+json + Retry-After; uses TimeProvider

OpenAPI (in host)

Route metadata (.Named / .Accepts / .Produces / .WithSecurity / …) builds the document.

app.MapElsieOpenApi(o =>
{
    o.Info.Title = "My API";
    o.Info.SecuritySchemes["ApiKey"] = ElsieOpenApiSecurityScheme.ApiKeyHeader();
    o.UiPath = "/scalar";
});

Views — Elsie.Views (Fluid / Liquid)

builder.Services.AddElsieViews(o => o.ContentRoot = builder.Environment.ContentRootPath);
return await ctx.ViewAsync("home", new { Title = "Hi", Name = "Ada" }, cancellationToken: ct);
{% layout '_Layout.liquid' %}
<h1>Hello {{ Name }}!</h1>

Static files (host)

app.MapElsieStaticFiles("/assets", Path.Combine(contentRoot, "wwwroot"));
// ETag / Last-Modified + 304; no range requests

Validation — Elsie.FluentValidation

var bind = await ctx.BindAndValidateJsonAsync<CreateTodo>(ct);

Testing — Elsie.Testing

await using var mem = ElsieInMemoryHost.Create(s => s.AddElsieModule<HelloModule>());
var r = await mem.GetAsync("/hello/Ada");
Assert.Equal(200, r.StatusCode);

await using var host = ElsieTestHost.Create(s => s.AddElsieModule<HelloModule>());
var response = await host.GetAsync("/hello/Ada");
response.AssertStatus(200);

In tests, set ScanEntryAssembly = false and register modules explicitly.


Package layout

Package Contents
Elsie Meta-package for apps → Elsie.AspNetCoreElsie.Core
Elsie.AspNetCore ElsieWeb, AddElsie, MapElsie, OpenAPI, static files
Elsie.Core Host-agnostic modules, routing, dispatcher, results, pipelines
Elsie.Auth Cookie/JWT + auth gates
Elsie.Cors Elsie-native CORS
Elsie.HealthChecks /healthz live/ready
Elsie.RateLimiting Before-hook rate limits
Elsie.Views Fluid/Liquid views
Elsie.FluentValidation BindAndValidateJsonAsync
Elsie.Testing In-memory + TestServer hosts
Elsie.Templates dotnet new elsie / elsie-api

Current version: 0.2.0-alpha.2 (prerelease; APIs may still change).

Namespaces stay Elsie / Elsie.AspNetCore regardless of package id. Library authors who want the host-agnostic surface only should reference Elsie.Core.


Request flow

HTTP request
  → host (ASP.NET Core or in-memory test host)
  → ElsieRequest
  → RouteTable.Lookup → before hooks → handler → after hooks → ElsieResult
  → ElsieHttpResponse.FromDispatch
  → host writes status / headers / body

MapElsie is non-terminal by default so OpenAPI, static files, and other endpoints can coexist. MapElsie(terminal: true) returns 404 problem+json for unmatched routes.


Docs

Topic
Getting started Install, smallest app, samples
Modules Registration, DI lifetimes
Routing Templates, constraints, precedence
Results Response factories
Binding Route/query/JSON/form
Pipelines & errors Before/after, exception maps
Auth Core gates + Elsie.Auth
CORS Elsie.Cors
Rate limiting Fixed/sliding windows
Health checks Live/ready
OpenAPI Document + Scalar
Views Fluid/Liquid
Static files MapElsieStaticFiles
Testing In-memory + TestServer
Hosting & AOT Host notes

Changelog: CHANGELOG.md


License

MIT © WavestormSoftwareLICENSE

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated