Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,21 @@
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<!-- guidance for coding agents, packed by the build-only Trellis.AgentDocs.Packaging helper; adds no runtime dependency -->
<ItemGroup Label="AgentDocs">
<PackageReference Include="Trellis.AgentDocs.Packaging" Version="0.1.0-preview.22" PrivateAssets="all" />
<PackageGuidanceItem Include="docs/agent-guide.md"
PackagePath="guides/agent-guide.md"
Usage="required"
Description="Read before adding or changing API versioning with Asp.Versioning.Http (Minimal APIs): registration, versioning endpoints, and default behavior." />
<PackageGuidanceItem Include="docs/version-sources-and-defaults.md"
PackagePath="guides/version-sources-and-defaults.md"
Description="Open when changing how clients send the API version (query string, header, URL segment) or what happens when none is sent." />
<PackageGuidanceItem Include="docs/deprecation-and-errors.md"
PackagePath="guides/deprecation-and-errors.md"
Description="Open when deprecating an API version, reading version headers, or changing unspecified, unsupported or invalid version responses." />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="Asp.Versioning.Mvc" Key="$(StrongNamePublicKey)" />
</ItemGroup>
Expand Down
50 changes: 50 additions & 0 deletions src/AspNetCore/WebApi/src/Asp.Versioning.Http/docs/agent-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Asp.Versioning.Http

`Asp.Versioning.Http` adds API versioning to ASP.NET Core **Minimal APIs**. Use
`Asp.Versioning.Mvc` as well when the service has controllers.

## Set up

```csharp
using Asp.Versioning;

builder.Services.AddProblemDetails();
builder.Services.AddApiVersioning( options => options.ReportApiVersions = true );
```

`ReportApiVersions = true` adds the `api-supported-versions` and `api-deprecated-versions`
response headers. Without it, clients get no version information in responses.

## Version the endpoints

Create one versioned API from the app, then declare the versions each endpoint implements:

```csharp
var api = app.NewVersionedApi();

api.MapGet( "/items", () => "version 1" ).HasApiVersion( 1.0 );
api.MapGet( "/items", () => "version 2" ).HasApiVersion( 2.0 );

// no version required
api.MapGet( "/ping", () => "pong" ).IsApiVersionNeutral();
```

`MapGroup( ... ).HasApiVersion( ... )` on the object returned by `NewVersionedApi()` versions every
endpoint in the group. A request is routed by the `api-version` query string parameter, for example
`GET /items?api-version=2.0`.

## Behavior you can rely on

- A request with **no** version to a versioned endpoint fails with **400** (`unspecified`).
- A version no endpoint supports fails with **400** (`unsupported`); a value that is not a version
(`api-version=banana`) fails with **400** (`invalid`). Both are `application/problem+json`.
- `api-version=1` is read as `1.0`.
- Call `NewVersionedApi()` on the app itself. Calling it on a `MapGroup` throws
`InvalidOperationException` ("A versioned API group cannot be mapped as a nested group.").

## More

- Change where clients send the version, or assume a default version:
[version sources and defaults](version-sources-and-defaults.md).
- Deprecate a version or change error responses:
[deprecation and errors](deprecation-and-errors.md).
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Deprecation and error responses

## Deprecate a version

Mark a version deprecated on the endpoints that still implement it:

```csharp
var api = app.NewVersionedApi();

api.MapGet( "/old", () => "old" )
.HasDeprecatedApiVersion( 0.9 )
.HasApiVersion( 1.0 );
```

With `ReportApiVersions = true`, `GET /old?api-version=0.9` still succeeds and the response carries
`api-supported-versions: 1.0, 2.0` and `api-deprecated-versions: 0.9`. The supported list covers all
versions in the API, not only the ones on that endpoint.

## Error responses

All of these are `application/problem+json` with `status` 400 by default:

| Request | `type` ends with |
|---|---|
| No version, no default assumed | `#unspecified` |
| A version nothing implements, such as `api-version=3.0` | `#unsupported` |
| A value that is not a version, such as `api-version=banana` | `#invalid` |

Change the status for unsupported versions with `options.UnsupportedApiVersionStatusCode = 404;`;
the response is still a `#unsupported` problem.

## Version-neutral endpoints

`IsApiVersionNeutral()` means no version is required: `GET /ping` and `GET /ping?api-version=1.0`
both succeed. A neutral endpoint still rejects a version nothing in the API supports:
`GET /ping?api-version=9.0` is a 400 `unsupported`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Version sources and defaults

## Where the version is read from

The default reader accepts the version from the `api-version` **query string** parameter and from a
**URL segment**. Setting `ApiVersionReader` **replaces** that default; it does not add to it.

```csharp
// header only: "?api-version=2.0" is now ignored and the request fails as unspecified
builder.Services.AddApiVersioning( options =>
options.ApiVersionReader = new HeaderApiVersionReader( "x-api-version" ) );
```

To accept several sources, combine them explicitly:

```csharp
builder.Services.AddApiVersioning( options =>
options.ApiVersionReader = ApiVersionReader.Combine(
new QueryStringApiVersionReader( "v" ),
new HeaderApiVersionReader( "x-api-version" ) ) );
```

With the combined reader above, `?v=1.0` and `x-api-version: 2.0` both work, and the default
`api-version` parameter no longer does.

## URL segment versioning

Put the `apiVersion` route constraint in the route template:

```csharp
var api = app.NewVersionedApi();
api.MapGet( "/v{version:apiVersion}/seg", () => "seg" ).HasApiVersion( 1.0 );
```

`GET /v1/seg` and `GET /v1.0/seg` succeed. A version no endpoint supports (`/v2/seg`) does **not**
produce the 400 `unsupported` problem: the route no longer matches and the response is **404**. A
request to `/seg?api-version=1.0` is also 404, because the template requires the segment.

## When the client sends no version

By default an unspecified version fails with 400. To serve a default version instead:

```csharp
builder.Services.AddApiVersioning( options =>
{
options.AssumeDefaultVersionWhenUnspecified = true;
options.DefaultApiVersion = new ApiVersion( 2, 0 ); // optional; the default is 1.0
} );
```

Assuming a default hides a missing version from clients, so only enable it for services that already had
unversioned clients.
Loading