RouteStub.Server 1.0.0

dotnet add package RouteStub.Server --version 1.0.0
                    
NuGet\Install-Package RouteStub.Server -Version 1.0.0
                    
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="RouteStub.Server" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RouteStub.Server" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="RouteStub.Server" />
                    
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 RouteStub.Server --version 1.0.0
                    
#r "nuget: RouteStub.Server, 1.0.0"
                    
#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 RouteStub.Server@1.0.0
                    
#: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=RouteStub.Server&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=RouteStub.Server&version=1.0.0
                    
Install as a Cake Tool

RouteStub

RouteStub is a small, convention-based HTTP stub server for .NET 10. A request path maps to folders, and two files define the response. There are no route mapping files, fluent route APIs, or custom DSLs.

Use it when an application needs to call a real HTTP endpoint during integration tests, frontend development, local development, or API prototyping.

GET /api/weather/sunny

stubs/
└── api/
    └── weather/
        └── sunny/
            ├── get.content.json
            └── get.response.json

What RouteStub can do

  • Serve static response bodies over real HTTP.
  • Resolve URL path segments to fixture directories.
  • Select fixtures by HTTP method.
  • Append query parameter values to the fixture path.
  • Match dynamic path or query-value segments with ~ wildcards.
  • Prefer an exact directory match over a wildcard match at each path segment.
  • Set the response status code and headers from JSON metadata.
  • Infer Content-Type from the content file extension.
  • Run as ASP.NET Core middleware, an in-process test server, or a CLI from this repository.
  • Replace the filesystem repository or resolver through ASP.NET Core dependency injection.

What RouteStub cannot do

RouteStub deliberately keeps request matching and response behavior simple. It does not currently:

  • Match request headers, cookies, or request bodies.
  • Match query parameter names; only values participate in resolution.
  • Make query parameter matching independent of parameter order.
  • Generate dynamic or templated response bodies.
  • Configure response delays.
  • Record or proxy traffic.
  • Model stateful scenarios or request sequences.
  • Perform contract validation or API virtualization.
  • Run scripts to calculate a response.
  • Fall through to later ASP.NET Core middleware after RouteStub handles a request.

RouteStub is not intended to replace feature-rich tools such as WireMock. It is for the case where a deterministic static response is enough.

Quick start with ASP.NET Core

Install the ASP.NET Core package:

dotnet add package RouteStub.AspNetCore

Create a stubs/api/weather directory with these two files:

stubs/api/weather/get.content.json

{
  "condition": "sunny",
  "temperatureC": 21
}

stubs/api/weather/get.response.json

{
  "statusCode": 200,
  "headers": {
    "X-Stub": "weather"
  }
}

Mount that directory in the ASP.NET Core pipeline:

using RouteStub.AspNetCore;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseStubServer("./stubs");
app.Run();

Then request GET /api/weather. RouteStub returns the JSON content with status 200, header X-Stub: weather, and content type application/json.

The fixture directory must exist when the application starts. Relative paths are resolved from the process working directory.

Fixture reference

Directory mapping

Each decoded URL path segment maps to one directory below the fixture root. Matching is case-insensitive.

/api/users/42  →  stubs/api/users/42/

An endpoint directory requires both of the following method-specific files:

<method>.content.<extension>
<method>.response.json

For example, one directory can provide different responses for GET and POST:

stubs/api/users/
├── get.content.json
├── get.response.json
├── post.content.json
└── post.response.json

File-name matching is case-insensitive. The supported method names are get, post, patch, put, delete, head, options, trace, query, and connect. Other HTTP methods receive 405 Method Not Allowed.

Each endpoint directory may contain exactly one content file for a given method. If, for example, both get.content.json and get.content.txt exist, RouteStub rejects the fixture as ambiguous instead of selecting one arbitrarily. Files outside the exact <method>.content.<extension> shape are ignored.

Response metadata

The <method>.response.json file is required, even when the default status code is sufficient. It accepts:

{
  "statusCode": 201,
  "headers": {
    "Location": "/api/users/42",
    "X-Environment": "stub"
  }
}
  • statusCode defaults to 200 when omitted.
  • statusCode must be between 100 and 999.
  • headers is optional and maps header names to string values.
  • Property names are matched case-insensitively.
  • Delay, body, and matching rules are not supported in this file.

Missing fixture directories and methods without fixture files result in 404 Not Found with an empty body. Incomplete, malformed, and ambiguous fixture configuration results in 500 Internal Server Error.

Direct core consumers receive InvalidFixtureException for a matched fixture that is missing one of its required files or has invalid response metadata. The exception provides a machine-readable Reason, the fixture-relative FixturePath, and the underlying JsonException in InnerException when JSON parsing fails:

try
{
    var (stub, response) = await resolver.ResolveAsync(request);
}
catch (InvalidFixtureException exception)
{
    Console.Error.WriteLine($"{exception.Reason}: {exception.FixturePath}");
    Console.Error.WriteLine(exception.Message);
}

Ambiguous fixtures throw AmbiguousStubException with all conflicting names.

Content types

The extension of <method>.content.* determines the response Content-Type:

Extensions Content type
.json application/json
.xml application/xml
.html, .htm text/html
.pdf application/pdf
.txt text/plain
.css text/css
.js, .mjs text/javascript
.csv text/csv
.yaml, .yml application/yaml
.png image/png
.jpg, .jpeg image/jpeg
.gif image/gif
.svg image/svg+xml
.webp image/webp

The body is returned as the file's raw bytes, so text and binary fixtures use the same convention. Other extensions are not supported.

Query parameters

Query parameter values are appended to the URL path in request enumeration order. Parameter names are not included.

GET /api/weather?city=London

stubs/
└── api/
    └── weather/
        └── London/
            ├── get.content.json
            └── get.response.json

For multiple parameters, create one nested directory per parameter value:

GET /search?category=books&page=2
→ stubs/search/books/2/

Because names are ignored, ?city=London and ?country=London resolve to the same fixture. Because values are appended in enumeration order, clients should send multiple parameters in a consistent order.

Wildcards

Use ~ inside a directory name to match zero or more characters in one path segment. A directory named only ~ matches any single segment.

stubs/api/users/
├── 42/
│   ├── get.content.json
│   └── get.response.json
└── ~/
    ├── get.content.json
    └── get.response.json

GET /api/users/42 uses the exact 42 directory. Other IDs, such as GET /api/users/99, use ~. Names such as user-~ are also valid patterns.

Wildcards match one directory level at a time; they do not consume multiple URL segments. An exact directory always wins without evaluating wildcard matches. If no exact directory exists and multiple wildcard directories match the same segment, RouteStub rejects the fixture as ambiguous and reports every matching directory.

Hosting options

All hosting options use the same resolver and fixture conventions.

ASP.NET Core middleware

For the shortest setup, call UseStubServer with a fixture path as shown in the quick start.

Use service registration when the path comes from configuration or the application provides a custom IStubRepository or IStubResolver:

using RouteStub.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddStubServer(options =>
{
    options.FixturePath = "./stubs";
});

var app = builder.Build();
app.UseStubServer();
app.Run();

The default repository and resolver are registered only when replacements are not already present in the service collection.

UseStubServer is terminal middleware: it responds to every request and does not call the next component. Register logging, authentication, CORS, or other middleware that must run for stub requests before it. Do not mount it in front of application endpoints that must remain reachable.

Integration tests

Install the framework-neutral test server package:

dotnet add package RouteStub.Testing

Start a real loopback HTTP server on an available port and point the client under test at it:

using RouteStub.Testing;

await using var server = await RouteStubTestServer.StartAsync("./stubs");

using var client = new HttpClient
{
    BaseAddress = server.BaseAddress
};

var response = await client.GetAsync("api/weather");

RouteStubTestServer supports startup cancellation and IAsyncDisposable. It does not depend on MSTest, xUnit, NUnit, or another test framework.

In-process server

Install RouteStub.Server when non-test application code needs to start and control a server directly:

dotnet add package RouteStub.Server
using RouteStub.Server;

await using var server = await StubServer.StartAsync("./stubs");
Console.WriteLine(server.BaseAddress);

Without a URL, StubServer listens on loopback using an available ephemeral port. Pass a URL as the second argument to select an address explicitly.

CLI from this repository

The CLI project is currently run from source. It defaults to the stubs directory and http://localhost:5000:

dotnet run --project src\RouteStub.Cli -- serve

Select another fixture directory or URL:

dotnet run --project src\RouteStub.Cli -- serve .\fixtures --url http://localhost:5050

Packages and requirements

RouteStub currently targets .NET 10.

Package Use it for
RouteStub.AspNetCore Mounting RouteStub in an ASP.NET Core pipeline
RouteStub.Testing Starting a framework-neutral server in integration tests
RouteStub.Server Starting and controlling an in-process server directly
RouteStub.Core Resolver models and custom repository/resolver implementations

Samples

Complete ASP.NET Core and integration-test examples are in samples.

dotnet build samples\RouteStub.Samples.slnx
dotnet test samples\RouteStub.Testing.Sample
dotnet run --project samples\RouteStub.AspNetCore.Sample
Product Compatible and additional computed target framework versions.
.NET 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 (1)

Showing the top 1 NuGet packages that depend on RouteStub.Server:

Package Downloads
RouteStub.Testing

Framework-neutral real HTTP test server for RouteStub integration tests.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 141 7/18/2026
0.0.1 122 7/17/2026