Servers
NSmithy generates one handler interface per service — a method per operation, in plain model types. You implement it once. The generated ASP.NET Core adapter converts each request, the shared server runtime dispatches it (deserialize → invoke → serialize, or serialize a modeled error), and the response goes back on the wire. None of that request-handling machinery depends on which protocol the service declares.
The service handler interface (IWeatherServiceHandler) is composed from one
per-operation interface for each operation — IGetCityHandler,
IListCitiesHandler, and so on — which it inherits:
public interface IGetCityHandler{ Task<GetCityOutput> GetCityAsync(GetCityInput input, CancellationToken ct = default);}
public interface IWeatherServiceHandler : IGetCityHandler, IListCitiesHandler { }Registration wires up both the aggregate interface and each per-operation
interface to your single implementation, so a component can depend on just the
one operation it needs (IGetCityHandler) instead of the whole service. The
model and the handler are the same across every protocol — see the Protocols
Overview for the shared model.
Register the handler
Section titled “Register the handler”Add{Service}Handler<THandler> registers one implementation of the whole
service against both the aggregate interface and each per-operation interface in
DI:
using Example.Weather;
var builder = WebApplication.CreateBuilder(args);builder.Services.AddWeatherServiceHandler<WeatherHandler>();Registering operation handlers separately
Section titled “Registering operation handlers separately”The generated endpoints resolve the per-operation interface from DI — never
the aggregate — so a single class implementing the whole service is a
convenience, not a requirement. If you’d rather split operations across classes
(or across teams), register each per-operation interface yourself and skip
AddWeatherServiceHandler:
builder.Services.AddSingleton<IGetCityHandler, GetCityHandler>();builder.Services.AddSingleton<IListCitiesHandler, ListCitiesHandler>();// …one registration per operation the mapped protocol servesEach mapped route picks up its operation’s handler independently. As long as every operation the protocol maps has a registration, the service is fully served.
Map the endpoints
Section titled “Map the endpoints”Each server generates a Map{Service} extension. By default it maps the first
declared server protocol; for multi-protocol services, pass the generated
{Service}Protocols flags enum to select one or more protocols:
var app = builder.Build();app.MapWeatherService();app.Run();The endpoint is thin: it binds the route to your handler method and the operation’s bound protocol, and delegates to the runtime. A service that declares several protocols can serve selected protocols from the same handler — see Hosting & Multiple Protocols.
Implement the handler
Section titled “Implement the handler”Return the modeled output; throw a generated error type to return a modeled error. The protocol serializes each with the correct status code and body:
internal sealed class WeatherHandler : IWeatherServiceHandler{ public Task<GetCityOutput> GetCityAsync( GetCityInput input, CancellationToken ct = default) { if (input.CityId == "unknown") throw new NoSuchResource(null, "City");
return Task.FromResult(new GetCityOutput("Seattle")); }}Error identity and status come from the model, so the same thrown exception serializes correctly for every protocol the service exposes.
A handler that implements a single per-operation interface looks the same, minus the other operations — implement just the one method:
internal sealed class GetCityHandler : IGetCityHandler{ public Task<GetCityOutput> GetCityAsync( GetCityInput input, CancellationToken ct = default) { if (input.CityId == "unknown") throw new NoSuchResource(null, "City");
return Task.FromResult(new GetCityOutput("Seattle")); }}Register it with AddSingleton<IGetCityHandler, GetCityHandler>() (see
Registering operation handlers separately).
The endpoint that maps GetCity resolves this handler directly, so it serves that
operation whether or not the rest of the service is implemented by the same class.
Constraint validation
Section titled “Constraint validation”The server checks the model’s constraint traits — @required, @length,
@range, @pattern, @uniqueItems — plus enum membership, after deserializing
the request and before calling the handler. A handler only sees input the model
permits, so it does not need to re-check what the model already states. A
violation gets smithy.framework#ValidationException with HTTP 400, listing
each one with a JSONPointer path into the input.
See Validation for what is checked, the response format, and what is not covered yet.
Which protocols generate a server
Section titled “Which protocols generate a server”Server generation exists for simpleRestJson, restJson1, rpcv2Cbor, and
gRPC. AWS JSON, AWS Query, EC2 Query, and restXml are client-only today. See
Protocol Status for the current matrix.
gRPC needs HTTP/2 transport and its own listener — see gRPC for the Kestrel setup.