Endpoint Routing

INFO

For docs on routing with ServiceStack prior to .NET 8 and v8.1 ServiceStack Routing

/api pre-defined route

Over the last decade JSON has stood out and become more popular than all others combined, where it's the lingua franca for calling APIs in Web Apps and what our Add ServiceStack Reference ecosystem of languages relies on. Due to its overwhelming dominance for usage in APIs it's configured as the default format for the pre-defined route at:

/api/{Request}

This simple convention makes it easy to remember what route APIs are available on & pairs nicely with API Explorer's:

/ui/{Request}

Configuring in .NET

No configuration is necessary for the new .NET 6+ JsonApiClient that's pre-configured to use /api fallback by default:

var client = new JsonApiClient(baseUri);

All .NET Clients use any matching user-defined routes defined on the Request DTO with the existing Service Clients falling back to /json/[reply|oneway] if none exist who can be configured to use the /api fallback with:

var client = new JsonServiceClient(baseUri) {
    UseBasePath = "/api"
};
var client = new JsonHttpClient(baseUri) {
    UseBasePath = "/api"
};

Other Service Clients

The latest versions of generic Service Clients in other languages are pre-configured to use /api by default:

JavaScript/TypeScript

const client = new JsonServiceClient(baseUrl)

Java/Kotlin

JsonServiceClient client = new JsonServiceClient(baseUrl);

Python

client = JsonServiceClient(baseUrl)

PHP

$client = new JsonServiceClient(baseUrl);

Dart

var client = ClientFactory.api(baseUrl);

Content Negotiation

The JSON API Route also supports returning API responses in multiple registered content types by using its extension, e.g:

/api/{Request}.{ext}

Query String Format

That continues to support specifying the Mime Type via the ?format query string, e.g:

Endpoint Routing

From ServiceStack v8.1 ServiceStack .NET 8 Apps support an integrated way to run all of ServiceStack requests including all APIs, metadata and built-in UIs with support for ASP.NET Core Endpoint Routing - enabled by calling MapEndpoints() when configuring ServiceStack:

services.AddServiceStack(typeof(MyServices).Assembly);

var app = builder.Build();
//...

app.UseServiceStack(new AppHost(), options => {
    options.MapEndpoints();
});

app.Run();

Which configures ServiceStack APIs to be registered and executed along-side Minimal APIs, Razor Pages, SignalR, MVC and Web API Controllers, etc, utilizing the same routing, metadata and execution pipeline.

Migrating to ASP.NET Core Endpoints

To assist ServiceStack users in upgrading their existing projects we've created a migration guide walking through the steps required to adopt these new defaults:

View ServiceStack APIs along-side ASP.NET Core APIs

Amongst other benefits, this integration is evident in endpoint metadata explorers like the Swashbuckle library which can now show ServiceStack APIs in its Swagger UI along-side other ASP.NET Core APIs in ServiceStack's Open API v3 support.

Routing Syntax

Using Endpoint Routing also means using ASP.NET Core's Routing System which now lets you use ASP.NET Core's Route constraints for defining user-defined routes for your ServiceStack APIs, e.g:

[Route("/users/{Id:int}")]
[Route("/users/{UserName:string}")]
public class GetUser : IGet, IReturn<User>
{
    public int? Id { get; set; }
    public int? UserName { get; set; }
}

For the most part ServiceStack Routing implements a subset of ASP.NET Core's Routing features so your existing user-defined routes should continue to work as expected.

Wildcard Routes

Wildcard or catch-all parameters can be used as a prefix to a route parameter to bind to the rest of the URI by using a * or ** prefix, e.g:

[Route("/wildcard/{*Path}")]  //escape path
[Route("/wildcard/{**Path}")] //leave unescaped
public class GetFile : IGet, IReturn<byte[]>
{
    public string Path { get; set; }
}

Route Constraints

The Route Constraints built into ASP.NET Core Routing include:

constraint Example Example Matches Notes
int {id:int} 123456789, -123456789 Matches any integer
bool {active:bool} true, FALSE Matches true or false. Case-insensitive
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm Matches a valid DateTime value in the invariant culture. See preceding warning.
decimal {price:decimal} 49.99, -1,000.01 Matches a valid decimal value in the invariant culture. See preceding warning.
double {weight:double} 1.234, -1,001.01e8 Matches a valid double value in the invariant culture. See preceding warning.
float {weight:float} 1.234, -1,001.01e8 Matches a valid float value in the invariant culture. See preceding warning.
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638 Matches a valid Guid value
long {ticks:long} 123456789, -123456789 Matches a valid long value
minlength(value) {username:minlength(4)} Rick String must be at least 4 characters
maxlength(value) {filename:maxlength(8)} MyFile String must be no more than 8 characters
length(length) {filename:length(12)} somefile.txt String must be exactly 12 characters long
length(min,max) {filename:length(8,16)} somefile.txt String must be at least 8 and no more than 16 characters long
min(value) {age:min(18)} 19 Integer value must be at least 18
max(value) {age:max(120)} 91 Integer value must be no more than 120
range(min,max) {age:range(18,120)} 91 Integer value must be at least 18 but no more than 120
alpha {name:alpha} Rick String must consist of one or more alphabetical characters, a-z and case-insensitive.
regex(expression) {ssn:regex(^\\d{‎{3}‎}-\\d{‎{2}‎}-\\d{‎{4}‎}$)} 123-45-6789 String must match the regular expression. See tips about defining a regular expression.
required {name:required} Rick Used to enforce that a non-parameter value is present during URL generation

Primary HTTP Method

An API will only register its Endpoint Route for its primary HTTP Method, if you want an API to be registered for multiple HTTP Methods you can specify them in the Route attribute, e.g:

[Route("/users/{Id:int}", "GET,POST")]
public class GetUser : IGet, IReturn<User>
{
    public required int Id { get; set; }
}

As such we recommend using the IVerb IGet, IPost, IPut, IPatch, IDelete interface markers to specify the primary HTTP Method for an API. This isn't needed for AutoQuery Services which are implicitly configured to use their optimal HTTP Method.

If no HTTP Method is specified, the Primary HTTP Method defaults to HTTP POST.

Authorization

Using Endpoint Routing also means ServiceStack's APIs are authorized the same way, where ServiceStack's Declarative Validation attributes are converted into ASP.NET Core's [Authorize] attribute to secure the endpoint:

[ValidateIsAuthenticated]
[ValidateIsAdmin]
[ValidateHasRole(role)]
[ValidateHasClaim(type,value)]
[ValidateHasScope(scope)]
public class Secured {}

Authorize Attribute on ServiceStack APIs

Alternatively you can use ASP.NET Core's [Authorize] attribute directly to secure ServiceStack APIs should you need more fine-grained Authorization:

[Authorize(Roles = "RequiredRole")]
[Authorize(Policy = "RequiredPolicy")]
[Authorize(AuthenticationSchemes = "Identity.Application,Bearer")]
public class Secured {}

Configuring Authentication Schemes

ServiceStack will default to using the major Authentication Schemes configured for your App to secure the APIs endpoint with, this can be overridden to specify which Authentication Schemes to use to restrict ServiceStack APIs by default, e.g:

app.UseServiceStack(new AppHost(), options => {
    options.AuthenticationSchemes = "Identity.Application,Bearer";
    options.MapEndpoints();
});

Hidden ServiceStack Endpoints

Whilst ServiceStack Requests are registered and executed as endpoints, most of them are marked with builder.ExcludeFromDescription() to hide them from polluting metadata and API Explorers like Swagger UI and API Explorer.

To also hide your ServiceStack APIs you can use [ExcludeMetadata] attribute to hide them from all metadata services or use [Exclude(Feature.ApiExplorer)] to just hide them from API Explorer UIs:

[ExcludeMetadata]
[Exclude(Feature.ApiExplorer)]
public class HiddenRequest {}

Customize Endpoint Mapping

You can register a RouteHandlerBuilders to customize how ServiceStack APIs endpoints are registered which is also what ServiceStack uses to annotate its API endpoints to enable its new Open API v3 support:

options.RouteHandlerBuilders.Add((builder, operation, method, route) =>
{
    builder.WithOpenApi(op => { ... });
});

Endpoint Routing Compatibility Levels

The default behavior of MapEndpoints() is the strictest and recommended configuration that we want future ServiceStack Apps to use, however if you're migrating existing App's you may want to relax these defaults to improve compatibility with existing behavior. The configurable defaults for mapping endpoints are:

app.UseServiceStack(new AppHost(), options => {
    options.MapEndpoints(use:true, force:true, useSystemJson:UseSystemJson.Always);
});
  • use - Whether to use registered endpoints for executing ServiceStack APIs
  • force - Whether to only allow APIs to be executed through endpoints
  • useSystemJson - Whether to use System.Text.Json for JSON API Serialization

So you could for instance register endpoints and not use them, where they'll be visible in endpoint API explorers like Swagger UI but continue to execute in ServiceStack's Request Pipeline.

force disables fallback execution of ServiceStack Requests through ServiceStack's Request Pipeline for requests that don't match registered endpoints. You may need to disable this if you have existing clients calling ServiceStack APIs through multiple HTTP Methods, as only the primary HTTP Method is registered as an endpoint.

When enabled force ensures the only ServiceStack Requests that are not executed through registered endpoints are IAppHost.CatchAllHandlers and IAppHost.FallbackHandler handlers.

useSystemJson lets you specify when to use System.Text.Json for JSON API Serialization, which enables your App to standardize on using ASP.NET Core's fast async UTF8 JSON Serializer.