Replies: 1 comment
|
I believe the I see the disconnect. API versions are ultimately just metadata and are applied via: public interface IApiVersionProvider
{
ApiVersionProviderOptions Options { get; }
IReadOnlyList<ApiVersion> Versions { get; }
}where the options are: [Flags]
public enum ApiVersionProviderOptions
{
None,
Deprecated = 1,
Advertised = 2,
Mapped = 4,
}The options are more comprehensive for scenarios than For better clarity, consider: [ApiController]
[ApiVersion(1.0, Deprecated = true)]
[ApiVersion(2.0)]
[Obsolete]
[Route("[controller]")]
public class ExampleController : ControllerBase
{
[HttpGet]
public IActionResult Get() => Ok();
}This is referred to as version interleaving. In this scenario, what does Custom conventions are supported, but the namespace My.Api.V1; // ← 1.0 is extracted from the namespace as a convention
[Obsolete] // ← the convention treats this ApiVersionProviderOptions.Deprecated
[Route("[controller]")]
public class ExampleController : ControllerBase
{
[HttpGet]
public IActionResult Get() => Ok();
}This applies to Minimal APIs as well. It's technically possible to interleave with this convention, but I don't think I've ever seen it. That makes it very hard to reason about. For more information, see the Version by Namespace wiki topic. The second miss is how policies work. The applied API versions are the source of truth. A policy does not set or change the API version for endpoints. It merely defines the policy for a particular API version. A policy is purely informational. The API Explorer extensions collate API versions by the endpoints that define them. If all endpoints for an API version are deprecated, then the entire API version is also deprecated. This is expressed and retrievable via the public class ApiVersionDescription
{
public ApiVersion ApiVersion { get; }
public string GroupName { get; }
public bool IsDeprecated { get; }
public SunsetPolicy? SunsetPolicy { get; }
public DeprecationPolicy? DeprecationPolicy { get; }
}All of the collation and associated policy information is gathered for you. This information is also available at the API level as well as the endpoint level. Although an endpoint is technically an API, that's not typically how service authors think about or refer to them. You would call it the Get Order operation on the Orders API. Deprecation and sunsetting usually occur on the entire API for a particular version, not individual endpoints. To circle back around as to why your configuration didn't meet your expectations. Define the Logical API FormallyAn API is a collated set of endpoints. If you give it a logical name, you never need to use app.NewVersionedApi("ToDo").MapGroup("api").MapToDoEndpoints();Define Deprecated API VersionsThe implementation is the source of truth. If you have/want a deprecated API version, then you need to express that. public static IEndpointRouteBuilder MapToDoEndpoints(this IEndpointRouteBuilder endpoints)
{
// applies to all endpoints in the group, but endpoints can express their versions independently too
var group = endpoints.MapGroup("ToDo").HasDeprecatedApiVersion(1);
group.MapGet("{id}", GetToDo);
group.MapGet("plain/{id}", GetPlainToDo);
return endpoints;
}Define a Deprecation PolicyThe deprecation policy doesn't deprecate an API version, it defines what the policy is for a deprecated API version. This information is output in client responses when reported and OpenAPI. The versioned options.Policies.Deprecate(1)
.Effective(new DateTimeOffset(2026, 10, 1))
.Link("policies/deprecation.html")
.Title("Version Deprecation Policy")
.Type("text/html");
options.Policies.Sunset(1)
.Effective(new DateTimeOffset(2027, 1, 1))
.Link("policies/sunset.html")
.Title("Version Sunset Policy")
.Type("text/html");This attaches a deprecation policy that informs clients that API version One of the reasons the policy is separate is because you can define an effective date in the future. This allows you to deploy a change that indicates when an API will become deprecated before it actually does. If no effective date is specified, it's assumed to be deprecated now. In a similar fashion, you may want to indicate the sunset policy. The difference is that sunsetting an API means it's gone for good whereas a deprecated API is still supported and addressable. The deprecation policy indicates when the API is deprecated and the sunset policy indicates when you're shutting it down and it won't exist anymore. These are closely related, but distinct policies. Each policy also yields different headers to clients about the policy. The A Deprecation Transformer is UnnecessaryAsp.Versioning.OpenApi uses the information from Asp.Versioning.Mvc.ApiExplorer to bridge the I hope that helps clarify things. |
Uh oh!
There was an error while loading. Please reload this page.
While trying to deprecate a single Minimal API endpoint without affecting other endpoints of the same version (as #1167 brought forth) I noticed some behavior I didn't expect and wanted to inquire if this is a bug or intended.
options.Policies.Deprecate(1)and enablingReportApiVersions, thedeprecatedfield is not set to true in the OpenAPI doc for any v1 endpoint and noapi-deprecated-versionsheader is returned. Is it intended that the deprecation policy does not really deprecate affected endpoints?OpenApiDeprecationTransformerto set the property to true on endpoints with theObsoleteAttribute, the endpoint still does not return anapi-deprecated-versionsheader. I realize the transformer only touches the document, so is there a way to set the endpoint earlier in the pipeline to deprecated so that the deprecation headers are handled correctly?HasDeprecatedApiVersion(1)on a specific endpoint, it is applied to the whole v1 version set. Is per-endpoint deprecation intentionally out of scope? If so, what's the recommended way to signal that a single endpoint is going away while the rest of the version stays current?I am under the impression that I have misunderstood a fundamental concept of this library which is giving me the wrong expectations.
Minimal reproduction example (tested on SDK 10.0.201):
All reactions