diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index b0c48605..e5d8caad 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -2,10 +2,10 @@
"version": 1,
"isRoot": true,
"tools": {
- "signclient": {
- "version": "1.3.155",
+ "sign": {
+ "version": "0.9.1-beta.23530.1",
"commands": [
- "SignClient"
+ "sign"
]
}
}
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 378b3123..e5eb2a2a 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,5 +1,5 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.177.0/containers/dotnet/.devcontainer/base.Dockerfile
# [Choice] .NET version: 7.0, 6.0, 5.0
-ARG VARIANT="6.0"
+ARG VARIANT="8.0"
FROM mcr.microsoft.com/vscode/devcontainers/dotnet
\ No newline at end of file
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index c40d7ef5..0ee6f006 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -20,7 +20,7 @@
"remoteEnv": {
"PATH": "${containerWorkspaceFolder}/.dotnet:${containerEnv:PATH}",
"DOTNET_MULTILEVEL_LOOKUP": "0",
- "TARGET": "net6.0",
+ "TARGET": "net8.0",
"DOTNET_WATCH_SUPPRESS_LAUNCH_BROWSER": "true"
},
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
diff --git a/.editorconfig b/.editorconfig
index fe5a4a9e..68505e1c 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -6,6 +6,8 @@ root = true
# don't use tabs for indentation
[*]
indent_style = space
+vsspell_section_id = 41b65011239a40959ccaae2a4ec7044a
+vsspell_ignored_words_41b65011239a40959ccaae2a4ec7044a = Accessor|app|clr|Edm|inline|middleware|Mvc|odata|Validator|Deconstruct
# code files
[*.{cs,csx,vb,vbx}]
@@ -88,8 +90,8 @@ csharp_space_between_method_declaration_parameter_list_parentheses = true
csharp_space_between_method_call_parameter_list_parentheses = true
csharp_space_between_parentheses = control_flow_statements, expressions
-# ide code suppressions
-# dotnet_diagnostic.IDE0079.severity = none
+# primary construcrtors
+csharp_style_prefer_primary_constructors = false:none
# style code suppressions
dotnet_diagnostic.SA1002.severity = none
@@ -110,6 +112,13 @@ dotnet_diagnostic.SA1502.severity = none
dotnet_diagnostic.SA1516.severity = none
dotnet_diagnostic.SA1600.severity = none
+# TEMP: currently suppressed rules due to false positives
+# REF: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687
+# REF: https://github.com/dotnet/aspnetcore/issues/52556
+dotnet_diagnostic.SA1010.severity = none # Opening square brackets should be spaced correctly
+dotnet_diagnostic.ASP0022.severity = none # Route conflict detected between route handlers
+dotnet_diagnostic.ASP0023.severity = none # Route conflict detected between route handlers
+
# test settings
# Default severity for analyzer diagnostics with category 'Reliability'
@@ -128,6 +137,7 @@ dotnet_diagnostic.CA1707.severity = none
dotnet_diagnostic.CA1711.severity = none
dotnet_diagnostic.CA1716.severity = none
dotnet_diagnostic.CA1806.severity = none
+dotnet_diagnostic.CA1861.severity = none
dotnet_diagnostic.CA2007.severity = none
dotnet_diagnostic.CA2234.severity = none
dotnet_code_quality.CA2000.excluded_symbol_names = HttpRequestMessage|HttpResponseMessage|HttpConfiguration|HttpRouteCollection|HostedHttpRouteCollection|HttpServer|HttpClient
@@ -138,6 +148,13 @@ dotnet_diagnostic.SA1300.severity = none
dotnet_diagnostic.SA1507.severity = none
dotnet_diagnostic.SA1601.severity = none
+# TEMP: currently suppressed rules due to false positives
+# REF: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687
+# REF: https://github.com/dotnet/aspnetcore/issues/52556
+dotnet_diagnostic.SA1010.severity = none # Opening square brackets should be spaced correctly
+dotnet_diagnostic.ASP0022.severity = none # Route conflict detected between route handlers
+dotnet_diagnostic.ASP0023.severity = none # Route conflict detected between route handlers
+
# test methods should use all lowercase characters
dotnet_naming_symbols.test_methods.applicable_kinds = method
dotnet_naming_symbols.test_methods.applicable_accessibilities = public
@@ -180,4 +197,4 @@ dotnet_naming_style.test_methods.word_separator = _
dotnet_naming_rule.test_methods.style = test_methods
dotnet_naming_rule.test_methods.symbols = test_methods
-dotnet_naming_rule.test_methods.severity = error
\ No newline at end of file
+dotnet_naming_rule.test_methods.severity = error
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 6978ee55..14290721 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -14,20 +14,4 @@ updates:
- "commonsensesoftware"
commit-message:
prefix: "[main] "
- include: scope
-
- # only servicing 5.x release
- - package-ecosystem: "nuget"
- directory: "/"
- target-branch: "release/5.0"
- schedule:
- interval: "monthly"
- allow:
- - dependency-type: "all"
- assignees:
- - "commonsensesoftware"
- reviewers:
- - "commonsensesoftware"
- commit-message:
- prefix: "[release/5.0] "
include: scope
\ No newline at end of file
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index f9b40dff..ec3be18c 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -29,8 +29,12 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
+ id: installdotnet
with:
- dotnet-version: 7.0.x
+ dotnet-version: 8.0.x
+
+ - name: Create temporary global.json
+ run: echo '{"sdk":{"version":"${{ steps.installdotnet.outputs.dotnet-version }}"}}' > ./global.json
# build a temporary *.slnf file that only contains source projects and put it in ~/obj
# so that it is not tracked by git. then run 'dotnet build' using the *.slnf, which
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 4bce8bd4..86a81124 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -26,10 +26,10 @@
"dotnet-test-explorer.runInParallel": true,
"dotnet-test-explorer.testProjectPath": "**/*Tests.csproj",
"editor.formatOnType": true,
- "omnisharp.enableImportCompletion": true,
"omnisharp.enableRoslynAnalyzers": true,
"omnisharp.organizeImportsOnFormat": true,
"omnisharp.useModernNet": true,
"omnisharp.enableMsBuildLoadProjectsOnDemand": true,
- "omnisharp.enableEditorConfigSupport": true
+ "omnisharp.enableEditorConfigSupport": true,
+ "dotnet.completion.showCompletionItemsFromUnimportedNamespaces": true
}
\ No newline at end of file
diff --git a/README.md b/README.md
index c0284787..d6828747 100644
--- a/README.md
+++ b/README.md
@@ -23,22 +23,6 @@ versioning in the past or supported API versioning with semantics that are diffe
The supported flavors of ASP.NET are:
-* **ASP.NET Web API**
-
Adds API versioning to your Web API applications
-
- [](https://www.nuget.org/packages/Asp.Versioning.WebApi)
- [](https://www.nuget.org/packages/Asp.Versioning.WebApi)
- [](../../wiki/New-Services-Quick-Start#aspnet-web-api)
- [](../../tree/main/examples/AspNet/WebApi)
-
-* **ASP.NET Web API and OData**
- Adds API versioning to your Web API applications using OData v4.0
-
- [](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData)
- [](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData)
- [](../../wiki/New-Services-Quick-Start#aspnet-web-api-with-odata-v40)
- [](../../tree/main/examples/AspNet/OData)
-
* **ASP.NET Core**
Adds API versioning to your ASP.NET Core Minimal API applications
@@ -63,23 +47,23 @@ The supported flavors of ASP.NET are:
[](../../wiki/New-Services-Quick-Start#aspnet-core-with-odata-v40)
[](../../tree/main/examples/AspNetCore/OData)
-This is also the home of the ASP.NET API versioning API explorers that you can use to easily document your REST APIs with OpenAPI:
+* **ASP.NET Web API**
+ Adds API versioning to your Web API applications
-* **ASP.NET Web API Versioned API Explorer**
- Replaces the default API explorer in your Web API applications
+ [](https://www.nuget.org/packages/Asp.Versioning.WebApi)
+ [](https://www.nuget.org/packages/Asp.Versioning.WebApi)
+ [](../../wiki/New-Services-Quick-Start#aspnet-web-api)
+ [](../../tree/main/examples/AspNet/WebApi)
- [](https://www.nuget.org/packages/Asp.Versioning.WebApi.ApiExplorer)
- [](https://www.nuget.org/packages/Asp.Versioning.WebApi.ApiExplorer)
- [](../../wiki/API-Documentation#aspnet-web-api)
- [](../../tree/main/examples/AspNet/WebApi/OpenApiWebApiSample)
+* **ASP.NET Web API and OData**
+ Adds API versioning to your Web API applications using OData v4.0
-* **ASP.NET Web API with OData API Explorer**
- Adds an API explorer to your Web API applications using OData v4.0
+ [](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData)
+ [](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData)
+ [](../../wiki/New-Services-Quick-Start#aspnet-web-api-with-odata-v40)
+ [](../../tree/main/examples/AspNet/OData)
- [](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData.ApiExplorer)
- [](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData.ApiExplorer)
- [](../../wiki/API-Documentation#aspnet-web-api-with-odata)
- [](../../tree/main/examples/AspNet/OData/OpenApiODataWebApiSample)
+This is also the home of the ASP.NET API versioning API explorers that you can use to easily document your REST APIs with OpenAPI:
* **ASP.NET Core Versioned API Explorer**
Adds additional API explorer support to your ASP.NET Core applications
@@ -97,6 +81,22 @@ This is also the home of the ASP.NET API versioning API explorers that you can u
[](../../wiki/API-Documentation#aspnet-core-with-odata)
[](../../tree/main/examples/AspNetCore/OData/OpenApiODataSample)
+* **ASP.NET Web API Versioned API Explorer**
+ Replaces the default API explorer in your Web API applications
+
+ [](https://www.nuget.org/packages/Asp.Versioning.WebApi.ApiExplorer)
+ [](https://www.nuget.org/packages/Asp.Versioning.WebApi.ApiExplorer)
+ [](../../wiki/API-Documentation#aspnet-web-api)
+ [](../../tree/main/examples/AspNet/WebApi/OpenApiWebApiSample)
+
+* **ASP.NET Web API with OData API Explorer**
+ Adds an API explorer to your Web API applications using OData v4.0
+
+ [](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData.ApiExplorer)
+ [](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData.ApiExplorer)
+ [](../../wiki/API-Documentation#aspnet-web-api-with-odata)
+ [](../../tree/main/examples/AspNet/OData/OpenApiODataWebApiSample)
+
The client-side libraries make it simple to create API version-aware HTTP clients.
* **HTTP Client API Versioning Extensions**
diff --git a/asp.sln b/asp.sln
index 57868d94..0df9e4ed 100644
--- a/asp.sln
+++ b/asp.sln
@@ -26,9 +26,6 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Abstractions", "Abstractions", "{7B0FA6C2-47BA-4C34-90E0-B75DF44F2124}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNet", "AspNet", "{34A0373F-12C9-44A8-9A1C-5EEE7218C877}"
- ProjectSection(SolutionItems) = preProject
- src\AspNet\Directory.Build.targets = src\AspNet\Directory.Build.targets
- EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNetCore", "AspNetCore", "{EBC9F217-E8BC-4DCE-9C67-F22150959EAF}"
EndProject
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index a9aa3dc4..f75e041a 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -2,6 +2,7 @@ trigger:
branches:
include:
- main
+ - release/*
paths:
exclude:
- .config
@@ -16,6 +17,7 @@ trigger:
pr:
- main
+- release/*
# build at least once a month so the build badge is up-to-date
schedules:
diff --git a/build/nuget.props b/build/nuget.props
index c913c57b..f679d6d3 100644
--- a/build/nuget.props
+++ b/build/nuget.props
@@ -26,16 +26,17 @@
+ true
+ snupkg
true
true
- $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
true
-
+
diff --git a/build/signing.json b/build/signing.json
deleted file mode 100644
index 3276a45d..00000000
--- a/build/signing.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "SignClient": {
- "AzureAd": {
- "AADInstance": "https://login.microsoftonline.com/",
- "ClientId": "c248d68a-ba6f-4aa9-8a68-71fe872063f8",
- "TenantId": "16076fdc-fcc1-4a15-b1ca-32c9a255900e"
- },
- "Service": {
- "Url": "https://codesign.dotnetfoundation.org/",
- "ResourceId": "https://SignService/3c30251f-36f3-490b-a955-520addb85001"
- }
- }
-}
\ No newline at end of file
diff --git a/build/steps-ci.yml b/build/steps-ci.yml
index 7ec23ad6..c2304d7d 100644
--- a/build/steps-ci.yml
+++ b/build/steps-ci.yml
@@ -11,7 +11,7 @@ steps:
displayName: Install .NET SDK
inputs:
packageType: sdk
- version: 7.0.x # https://github.com/dotnet/core/blob/main/release-notes/releases-index.json
+ version: 8.0.x # https://github.com/dotnet/core/blob/main/release-notes/releases-index.json
- task: DotNetCoreCLI@2
displayName: Build and Test
diff --git a/build/steps-release.yml b/build/steps-release.yml
index 60fd3c3a..ca070468 100644
--- a/build/steps-release.yml
+++ b/build/steps-release.yml
@@ -16,41 +16,32 @@ steps:
command: pack
projects: ${{ parameters.solution }}
${{ if eq(parameters.versionSuffix, '') }}:
- arguments: --configuration ${{ parameters.configuration }}
+ arguments: --no-build --configuration ${{ parameters.configuration }}
${{ else }}:
- arguments: --configuration ${{ parameters.configuration }} --version-suffix ${{ parameters.versionSuffix }}
+ arguments: --no-build --configuration ${{ parameters.configuration }} --version-suffix ${{ parameters.versionSuffix }}
outputDir: $(Build.ArtifactStagingDirectory)/packages
noBuild: true
- script: dotnet tool restore
displayName: Restore Tools
-- pwsh: >
- Compress-Archive
- -Path $(Build.ArtifactStagingDirectory)/packages/*
- -DestinationPath $(Build.ArtifactStagingDirectory)/packages.zip
- displayName: Package Artifacts for Signing
-
- script: >
- dotnet signclient sign
- --config build/signing.json
- --input $(Build.ArtifactStagingDirectory)/packages.zip
- --user "$(codesign_user)"
- --secret "$(codesign_secret)"
- --name "ASP.NET API Versioning"
+ dotnet sign code azure-key-vault "*.nupkg"
+ --base-directory "$(Build.ArtifactStagingDirectory)/packages"
+ --publisher-name "ASP.NET API Versioning"
--description "Adds versioning semantics to APIs built with ASP.NET"
- --descriptionUrl "https://github.com/dotnet/aspnet-api-versioning"
+ --description-url "https://github.com/dotnet/aspnet-api-versioning"
+ --azure-key-vault-tenant-id "$(SignTenantId)"
+ --azure-key-vault-client-id "$(SignClientId)"
+ --azure-key-vault-client-secret "$(SignClientSecret)"
+ --azure-key-vault-certificate "$(SignKeyVaultCertificate)"
+ --azure-key-vault-url "$(SignKeyVaultUrl)"
+ --timestamp-url http://timestamp.digicert.com
displayName: Sign Artifacts
-- pwsh: >
- Expand-Archive
- -Path $(Build.ArtifactStagingDirectory)/packages.zip
- -DestinationPath $(Build.ArtifactStagingDirectory)/signed-packages
- displayName: Extract Signed Artifacts
-
- task: PublishBuildArtifacts@1
- displayName: Publish package artifacts
+ displayName: Publish Artifacts
inputs:
- pathToPublish: $(Build.ArtifactStagingDirectory)/signed-packages
+ pathToPublish: $(Build.ArtifactStagingDirectory)/packages
publishLocation: Container
artifactName: NuGet Packages
\ No newline at end of file
diff --git a/build/test.targets b/build/test.targets
index 286e53f6..a3fc5fea 100644
--- a/build/test.targets
+++ b/build/test.targets
@@ -3,8 +3,13 @@
6.8.0
- 4.18.2
- 2.4.5
+
+
+ 4.20.69
+ 2.5.0
@@ -15,14 +20,14 @@
-
+
-
+
-
+
diff --git a/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/PersonModelConfiguration.cs b/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/PersonModelConfiguration.cs
index 86759c34..aa3c7000 100644
--- a/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/PersonModelConfiguration.cs
+++ b/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/PersonModelConfiguration.cs
@@ -18,6 +18,7 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string rout
person.HasKey( p => p.Id );
person.Select().OrderBy( "firstName", "lastName" );
+ person.Page( maxTopValue: 100, pageSizeValue: default );
if ( apiVersion < ApiVersions.V3 )
{
diff --git a/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/ProductConfiguration.cs b/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/ProductConfiguration.cs
index b6c151f0..edc33de9 100644
--- a/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/ProductConfiguration.cs
+++ b/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/ProductConfiguration.cs
@@ -18,8 +18,10 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string rout
return;
}
- var product = builder.EntitySet( "Products" ).EntityType.HasKey( p => p.Id );
+ var product = builder.EntitySet( "Products" ).EntityType;
+ product.HasKey( p => p.Id );
+ product.Page( maxTopValue: 100, pageSizeValue: default );
product.Action( "Rate" ).Parameter( "stars" );
product.Collection.Action( "Rate" ).Parameter( "stars" );
}
diff --git a/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/SupplierConfiguration.cs b/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/SupplierConfiguration.cs
index f6e2f817..b1f91df2 100644
--- a/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/SupplierConfiguration.cs
+++ b/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/SupplierConfiguration.cs
@@ -18,7 +18,11 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string rout
return;
}
- builder.EntitySet( "Suppliers" ).EntityType.HasKey( p => p.Id );
+ var supplier = builder.EntitySet( "Suppliers" ).EntityType;
+
+ supplier.HasKey( p => p.Id );
+ supplier.Page( maxTopValue: 100, pageSizeValue: default );
+
builder.Singleton( "Acme" );
}
}
\ No newline at end of file
diff --git a/examples/AspNet/OData/OpenApiODataWebApiExample/Models/Order.cs b/examples/AspNet/OData/OpenApiODataWebApiExample/Models/Order.cs
index 6ed326dc..e3f15cc4 100644
--- a/examples/AspNet/OData/OpenApiODataWebApiExample/Models/Order.cs
+++ b/examples/AspNet/OData/OpenApiODataWebApiExample/Models/Order.cs
@@ -7,6 +7,7 @@
///
/// Represents an order.
///
+[Page( MaxTop = 100 )]
[Select]
[Select( "effectiveDate", SelectType = SelectExpandType.Disabled )]
public class Order
diff --git a/examples/AspNet/OData/OpenApiODataWebApiExample/Startup.cs b/examples/AspNet/OData/OpenApiODataWebApiExample/Startup.cs
index e867f18d..2577d01c 100644
--- a/examples/AspNet/OData/OpenApiODataWebApiExample/Startup.cs
+++ b/examples/AspNet/OData/OpenApiODataWebApiExample/Startup.cs
@@ -5,6 +5,7 @@
using Asp.Versioning.Conventions;
using Asp.Versioning.OData;
using Microsoft.AspNet.OData.Extensions;
+using Microsoft.Extensions.Primitives;
using Microsoft.OData;
using Newtonsoft.Json.Serialization;
using Owin;
@@ -118,9 +119,9 @@ public void Configuration( IAppBuilder builder )
description.Append( " This API version has been deprecated." );
}
- if ( group.SunsetPolicy is SunsetPolicy policy )
+ if ( group.SunsetPolicy is { } policy )
{
- if ( policy.Date is DateTimeOffset when )
+ if ( policy.Date is { } when )
{
description.Append( " The API will be sunset on " )
.Append( when.Date.ToShortDateString() )
@@ -131,25 +132,45 @@ public void Configuration( IAppBuilder builder )
{
description.AppendLine();
+ var rendered = false;
+
for ( var i = 0; i < policy.Links.Count; i++ )
{
var link = policy.Links[i];
if ( link.Type == "text/html" )
{
- description.AppendLine();
-
- if ( link.Title.HasValue )
+ if ( !rendered )
{
- description.Append( link.Title.Value ).Append( ": " );
+ description.AppendLine();
+ description.Append( "**Links**" );
+ description.AppendLine();
+ rendered = true;
}
- description.Append( link.LinkTarget.OriginalString );
+ if ( StringSegment.IsNullOrEmpty( link.Title ) )
+ {
+ if ( link.LinkTarget.IsAbsoluteUri )
+ {
+ description.AppendLine( $"- {link.LinkTarget.OriginalString}" );
+ }
+ else
+ {
+ description.AppendFormat( "- {0} ", link.LinkTarget.OriginalString );
+ description.AppendLine();
+ }
+ }
+ else
+ {
+ description.AppendLine( $"- [{link.Title}]({link.LinkTarget.OriginalString})" );
+ }
}
}
}
}
+ description.AppendLine();
+ description.AppendLine( "**Additional Information**" );
info.Version( group.Name, $"Sample API {group.ApiVersion}" )
.Contact( c => c.Name( "Bill Mei" ).Email( "bill.mei@somewhere.com" ) )
.Description( description.ToString() )
diff --git a/examples/AspNet/OData/OpenApiODataWebApiExample/SwaggerDefaultValues.cs b/examples/AspNet/OData/OpenApiODataWebApiExample/SwaggerDefaultValues.cs
index 67cd7607..2ce56dcf 100644
--- a/examples/AspNet/OData/OpenApiODataWebApiExample/SwaggerDefaultValues.cs
+++ b/examples/AspNet/OData/OpenApiODataWebApiExample/SwaggerDefaultValues.cs
@@ -30,17 +30,11 @@ public void Apply( Operation operation, SchemaRegistry schemaRegistry, ApiDescri
var description = apiDescription.ParameterDescriptions.First( p => p.Name == parameter.name );
// REF: https://github.com/domaindrivendev/Swashbuckle/issues/1101
- if ( parameter.description == null )
- {
- parameter.description = description.Documentation;
- }
+ parameter.description ??= description.Documentation;
// REF: https://github.com/domaindrivendev/Swashbuckle/issues/1089
// REF: https://github.com/domaindrivendev/Swashbuckle/pull/1090
- if ( parameter.@default == null )
- {
- parameter.@default = description.ParameterDescriptor?.DefaultValue;
- }
+ parameter.@default ??= description.ParameterDescriptor?.DefaultValue;
}
}
}
\ No newline at end of file
diff --git a/examples/AspNet/OData/OpenApiODataWebApiExample/V3/SuppliersController.cs b/examples/AspNet/OData/OpenApiODataWebApiExample/V3/SuppliersController.cs
index 6e1412da..af837960 100644
--- a/examples/AspNet/OData/OpenApiODataWebApiExample/V3/SuppliersController.cs
+++ b/examples/AspNet/OData/OpenApiODataWebApiExample/V3/SuppliersController.cs
@@ -118,7 +118,7 @@ public IHttpActionResult Put( [FromODataUri] int key, [FromBody] Supplier update
///
/// The supplier identifier.
/// The associated supplier products.
- [EnableQuery]
+ [EnableQuery( MaxTop = 100 )]
public IQueryable GetProducts( [FromODataUri] int key ) =>
suppliers.Where( s => s.Id == key ).SelectMany( s => s.Products );
diff --git a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Book.cs b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Book.cs
index bd9fae58..a0a736b0 100644
--- a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Book.cs
+++ b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Book.cs
@@ -1,8 +1,15 @@
namespace ApiVersioning.Examples;
+using Microsoft.AspNet.OData.Query;
+
+// TODO: Model Bound settings can be performed via attributes if the
+// return type is known to the API Explorer or can be explicitly done
+// via one or more IModelConfiguration implementations
+
///
/// Represents a book.
///
+[Filter( "author", "published" )]
public class Book
{
///
diff --git a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/BooksController.cs b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/BooksController.cs
index 05dbf26b..6017627b 100644
--- a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/BooksController.cs
+++ b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/BooksController.cs
@@ -14,15 +14,15 @@
[RoutePrefix( "api/books" )]
public class BooksController : ApiController
{
- private static readonly Book[] books = new Book[]
- {
+ private static readonly Book[] books =
+ [
new() { Id = "9781847490599", Title = "Anna Karenina", Author = "Leo Tolstoy", Published = 1878 },
new() { Id = "9780198800545", Title = "War and Peace", Author = "Leo Tolstoy", Published = 1869 },
new() { Id = "9780684801520", Title = "The Great Gatsby", Author = "F. Scott Fitzgerald", Published = 1925 },
new() { Id = "9780486280615", Title = "The Adventures of Huckleberry Finn", Author = "Mark Twain", Published = 1884 },
new() { Id = "9780140430820", Title = "Moby Dick", Author = "Herman Melville", Published = 1851 },
new() { Id = "9780060934347", Title = "Don Quixote", Author = "Miguel de Cervantes", Published = 1605 },
- };
+ ];
///
/// Gets all books.
diff --git a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs
index 73be263e..1ac7209a 100644
--- a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs
+++ b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs
@@ -1,8 +1,8 @@
namespace ApiVersioning.Examples;
-using Asp.Versioning;
using Asp.Versioning.Conventions;
using Microsoft.AspNet.OData.Extensions;
+using Microsoft.Extensions.Primitives;
using Microsoft.OData;
using Newtonsoft.Json.Serialization;
using Owin;
@@ -68,6 +68,10 @@ public void Configuration( IAppBuilder builder )
.Allow( Skip | Count )
.AllowTop( 100 )
.AllowOrderBy( "title", "published" );
+
+ // applies model bound settings implicitly using an ad hoc EDM. alternatively, you can create your own
+ // IModelConfiguration + IODataQueryOptionsConvention for full control over what goes in the ad hoc EDM.
+ options.AdHocModelBuilder.ModelConfigurations.Add( new ImplicitModelBoundSettingsConvention() );
} );
configuration.EnableSwagger(
@@ -88,9 +92,9 @@ public void Configuration( IAppBuilder builder )
description.Append( " This API version has been deprecated." );
}
- if ( group.SunsetPolicy is SunsetPolicy policy )
+ if ( group.SunsetPolicy is { } policy )
{
- if ( policy.Date is DateTimeOffset when )
+ if ( policy.Date is { } when )
{
description.Append( " The API will be sunset on " )
.Append( when.Date.ToShortDateString() )
@@ -101,25 +105,45 @@ public void Configuration( IAppBuilder builder )
{
description.AppendLine();
+ var rendered = false;
+
for ( var i = 0; i < policy.Links.Count; i++ )
{
var link = policy.Links[i];
if ( link.Type == "text/html" )
{
- description.AppendLine();
-
- if ( link.Title.HasValue )
+ if ( !rendered )
{
- description.Append( link.Title.Value ).Append( ": " );
+ description.AppendLine();
+ description.Append( "**Links**" );
+ description.AppendLine();
+ rendered = true;
}
- description.Append( link.LinkTarget.OriginalString );
+ if ( StringSegment.IsNullOrEmpty( link.Title ) )
+ {
+ if ( link.LinkTarget.IsAbsoluteUri )
+ {
+ description.AppendLine( $"- {link.LinkTarget.OriginalString}" );
+ }
+ else
+ {
+ description.AppendFormat( "- {0} ", link.LinkTarget.OriginalString );
+ description.AppendLine();
+ }
+ }
+ else
+ {
+ description.AppendLine( $"- [{link.Title}]({link.LinkTarget.OriginalString})" );
+ }
}
}
}
}
+ description.AppendLine();
+ description.AppendLine( "**Additional Information**" );
info.Version( group.Name, $"Sample API {group.ApiVersion}" )
.Contact( c => c.Name( "Bill Mei" ).Email( "bill.mei@somewhere.com" ) )
.Description( description.ToString() )
diff --git a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/SwaggerDefaultValues.cs b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/SwaggerDefaultValues.cs
index 67cd7607..2ce56dcf 100644
--- a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/SwaggerDefaultValues.cs
+++ b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/SwaggerDefaultValues.cs
@@ -30,17 +30,11 @@ public void Apply( Operation operation, SchemaRegistry schemaRegistry, ApiDescri
var description = apiDescription.ParameterDescriptions.First( p => p.Name == parameter.name );
// REF: https://github.com/domaindrivendev/Swashbuckle/issues/1101
- if ( parameter.description == null )
- {
- parameter.description = description.Documentation;
- }
+ parameter.description ??= description.Documentation;
// REF: https://github.com/domaindrivendev/Swashbuckle/issues/1089
// REF: https://github.com/domaindrivendev/Swashbuckle/pull/1090
- if ( parameter.@default == null )
- {
- parameter.@default = description.ParameterDescriptor?.DefaultValue;
- }
+ parameter.@default ??= description.ParameterDescriptor?.DefaultValue;
}
}
}
\ No newline at end of file
diff --git a/examples/AspNet/WebApi/BasicWebApiExample/Controllers/Values2Controller.cs b/examples/AspNet/WebApi/BasicWebApiExample/Controllers/Values2Controller.cs
index 7dd4690b..ef7aa0ba 100644
--- a/examples/AspNet/WebApi/BasicWebApiExample/Controllers/Values2Controller.cs
+++ b/examples/AspNet/WebApi/BasicWebApiExample/Controllers/Values2Controller.cs
@@ -1,6 +1,7 @@
namespace ApiVersioning.Examples.Controllers;
using Asp.Versioning;
+using System.Net.Http;
using System.Web.Http;
[ApiVersion( 2.0 )]
diff --git a/examples/AspNet/WebApi/ConventionsWebApiExample/Controllers/Values2Controller.cs b/examples/AspNet/WebApi/ConventionsWebApiExample/Controllers/Values2Controller.cs
index e8ceaf54..1d700c5c 100644
--- a/examples/AspNet/WebApi/ConventionsWebApiExample/Controllers/Values2Controller.cs
+++ b/examples/AspNet/WebApi/ConventionsWebApiExample/Controllers/Values2Controller.cs
@@ -1,5 +1,6 @@
namespace ApiVersioning.Examples.Controllers;
+using System.Net.Http;
using System.Web.Http;
[RoutePrefix( "api/values" )]
diff --git a/examples/AspNet/WebApi/OpenApiWebApiExample/Startup.cs b/examples/AspNet/WebApi/OpenApiWebApiExample/Startup.cs
index b3651855..2a060f11 100644
--- a/examples/AspNet/WebApi/OpenApiWebApiExample/Startup.cs
+++ b/examples/AspNet/WebApi/OpenApiWebApiExample/Startup.cs
@@ -2,6 +2,7 @@
using Asp.Versioning;
using Asp.Versioning.Routing;
+using Microsoft.Extensions.Primitives;
using Owin;
using Swashbuckle.Application;
using System.IO;
@@ -75,9 +76,9 @@ public void Configuration( IAppBuilder builder )
description.Append( " This API version has been deprecated." );
}
- if ( group.SunsetPolicy is SunsetPolicy policy )
+ if ( group.SunsetPolicy is { } policy )
{
- if ( policy.Date is DateTimeOffset when )
+ if ( policy.Date is { } when )
{
description.Append( " The API will be sunset on " )
.Append( when.Date.ToShortDateString() )
@@ -88,25 +89,45 @@ public void Configuration( IAppBuilder builder )
{
description.AppendLine();
+ var rendered = false;
+
for ( var i = 0; i < policy.Links.Count; i++ )
{
var link = policy.Links[i];
if ( link.Type == "text/html" )
{
- description.AppendLine();
-
- if ( link.Title.HasValue )
+ if ( !rendered )
{
- description.Append( link.Title.Value ).Append( ": " );
+ description.AppendLine();
+ description.Append( "**Links**" );
+ description.AppendLine();
+ rendered = true;
}
- description.Append( link.LinkTarget.OriginalString );
+ if ( StringSegment.IsNullOrEmpty( link.Title ) )
+ {
+ if ( link.LinkTarget.IsAbsoluteUri )
+ {
+ description.AppendLine( $"- {link.LinkTarget.OriginalString}" );
+ }
+ else
+ {
+ description.AppendFormat( "- {0} ", link.LinkTarget.OriginalString );
+ description.AppendLine();
+ }
+ }
+ else
+ {
+ description.AppendLine( $"- [{link.Title}]({link.LinkTarget.OriginalString})" );
+ }
}
}
}
}
+ description.AppendLine();
+ description.AppendLine( "**Additional Information**" );
info.Version( group.Name, $"Example API {group.ApiVersion}" )
.Contact( c => c.Name( "Bill Mei" ).Email( "bill.mei@somewhere.com" ) )
.Description( description.ToString() )
diff --git a/examples/AspNet/WebApi/OpenApiWebApiExample/SwaggerDefaultValues.cs b/examples/AspNet/WebApi/OpenApiWebApiExample/SwaggerDefaultValues.cs
index 2fa156c5..dc26ff66 100644
--- a/examples/AspNet/WebApi/OpenApiWebApiExample/SwaggerDefaultValues.cs
+++ b/examples/AspNet/WebApi/OpenApiWebApiExample/SwaggerDefaultValues.cs
@@ -30,17 +30,11 @@ public void Apply( Operation operation, SchemaRegistry schemaRegistry, ApiDescri
var description = apiDescription.ParameterDescriptions.First( p => p.Name == parameter.name );
// REF: https://github.com/domaindrivendev/Swashbuckle/issues/1101
- if ( parameter.description == null )
- {
- parameter.description = description.Documentation;
- }
+ parameter.description ??= description.Documentation;
// REF: https://github.com/domaindrivendev/Swashbuckle/issues/1089
// REF: https://github.com/domaindrivendev/Swashbuckle/pull/1090
- if ( parameter.@default == null )
- {
- parameter.@default = description.ParameterDescriptor?.DefaultValue;
- }
+ parameter.@default ??= description.ParameterDescriptor?.DefaultValue;
}
}
}
\ No newline at end of file
diff --git a/src/AspNet/Directory.Build.targets b/examples/AspNetCore/Directory.Build.props
similarity index 59%
rename from src/AspNet/Directory.Build.targets
rename to examples/AspNetCore/Directory.Build.props
index 6a993f01..28e9057c 100644
--- a/src/AspNet/Directory.Build.targets
+++ b/examples/AspNetCore/Directory.Build.props
@@ -3,8 +3,8 @@
-
-
-
+
+ net8.0
+
\ No newline at end of file
diff --git a/examples/AspNetCore/OData/Directory.Build.props b/examples/AspNetCore/OData/Directory.Build.props
index 0d88b71c..0c6cf639 100644
--- a/examples/AspNetCore/OData/Directory.Build.props
+++ b/examples/AspNetCore/OData/Directory.Build.props
@@ -1,11 +1,10 @@
-
+
-
+
\ No newline at end of file
diff --git a/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/OrderModelConfiguration.cs b/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/OrderModelConfiguration.cs
index af356c02..b0a79db7 100644
--- a/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/OrderModelConfiguration.cs
+++ b/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/OrderModelConfiguration.cs
@@ -9,7 +9,7 @@ public class OrderModelConfiguration : IModelConfiguration
{
private static readonly ApiVersion V2 = new( 2, 0 );
- private EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder )
+ private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder )
{
var order = builder.EntitySet( "Orders" ).EntityType;
diff --git a/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/PersonModelConfiguration.cs b/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/PersonModelConfiguration.cs
index 0ab0aed8..f462cb9a 100644
--- a/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/PersonModelConfiguration.cs
+++ b/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/PersonModelConfiguration.cs
@@ -7,16 +7,16 @@
public class PersonModelConfiguration : IModelConfiguration
{
- private void ConfigureV1( ODataModelBuilder builder )
+ private static void ConfigureV1( ODataModelBuilder builder )
{
var person = ConfigureCurrent( builder );
person.Ignore( p => p.Email );
person.Ignore( p => p.Phone );
}
- private void ConfigureV2( ODataModelBuilder builder ) => ConfigureCurrent( builder ).Ignore( p => p.Phone );
+ private static void ConfigureV2( ODataModelBuilder builder ) => ConfigureCurrent( builder ).Ignore( p => p.Phone );
- private EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder )
+ private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder )
{
var person = builder.EntitySet( "People" ).EntityType;
diff --git a/examples/AspNetCore/OData/ODataAdvancedExample/ODataAdvancedExample.csproj b/examples/AspNetCore/OData/ODataAdvancedExample/ODataAdvancedExample.csproj
index f0d91cbe..ecccb3a9 100644
--- a/examples/AspNetCore/OData/ODataAdvancedExample/ODataAdvancedExample.csproj
+++ b/examples/AspNetCore/OData/ODataAdvancedExample/ODataAdvancedExample.csproj
@@ -1,9 +1,5 @@
-
- net7.0
-
-
diff --git a/examples/AspNetCore/OData/ODataAdvancedExample/Program.cs b/examples/AspNetCore/OData/ODataAdvancedExample/Program.cs
index 0c88e968..e12ad205 100644
--- a/examples/AspNetCore/OData/ODataAdvancedExample/Program.cs
+++ b/examples/AspNetCore/OData/ODataAdvancedExample/Program.cs
@@ -6,6 +6,7 @@
// Add services to the container.
builder.Services.AddControllers().AddOData();
+builder.Services.AddProblemDetails();
builder.Services.AddApiVersioning(
options =>
{
diff --git a/examples/AspNetCore/OData/ODataBasicExample/Configuration/OrderModelConfiguration.cs b/examples/AspNetCore/OData/ODataBasicExample/Configuration/OrderModelConfiguration.cs
index 26573aec..c757921a 100644
--- a/examples/AspNetCore/OData/ODataBasicExample/Configuration/OrderModelConfiguration.cs
+++ b/examples/AspNetCore/OData/ODataBasicExample/Configuration/OrderModelConfiguration.cs
@@ -9,7 +9,7 @@ public class OrderModelConfiguration : IModelConfiguration
{
private static readonly ApiVersion V1 = new( 1, 0 );
- private EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder )
+ private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder )
{
var order = builder.EntitySet( "Orders" ).EntityType;
diff --git a/examples/AspNetCore/OData/ODataBasicExample/Configuration/PersonModelConfiguration.cs b/examples/AspNetCore/OData/ODataBasicExample/Configuration/PersonModelConfiguration.cs
index a0d2c5f8..5ebaa0db 100644
--- a/examples/AspNetCore/OData/ODataBasicExample/Configuration/PersonModelConfiguration.cs
+++ b/examples/AspNetCore/OData/ODataBasicExample/Configuration/PersonModelConfiguration.cs
@@ -7,16 +7,16 @@
public class PersonModelConfiguration : IModelConfiguration
{
- private void ConfigureV1( ODataModelBuilder builder )
+ private static void ConfigureV1( ODataModelBuilder builder )
{
var person = ConfigureCurrent( builder );
person.Ignore( p => p.Email );
person.Ignore( p => p.Phone );
}
- private void ConfigureV2( ODataModelBuilder builder ) => ConfigureCurrent( builder ).Ignore( p => p.Phone );
+ private static void ConfigureV2( ODataModelBuilder builder ) => ConfigureCurrent( builder ).Ignore( p => p.Phone );
- private EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder )
+ private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder )
{
var person = builder.EntitySet( "People" ).EntityType;
diff --git a/examples/AspNetCore/OData/ODataBasicExample/ODataBasicExample.csproj b/examples/AspNetCore/OData/ODataBasicExample/ODataBasicExample.csproj
index fc184237..6bdc8cef 100644
--- a/examples/AspNetCore/OData/ODataBasicExample/ODataBasicExample.csproj
+++ b/examples/AspNetCore/OData/ODataBasicExample/ODataBasicExample.csproj
@@ -1,9 +1,5 @@
-
- net7.0
-
-
diff --git a/examples/AspNetCore/OData/ODataBasicExample/Program.cs b/examples/AspNetCore/OData/ODataBasicExample/Program.cs
index 420bfaca..a269f238 100644
--- a/examples/AspNetCore/OData/ODataBasicExample/Program.cs
+++ b/examples/AspNetCore/OData/ODataBasicExample/Program.cs
@@ -5,6 +5,7 @@
// Add services to the container.
builder.Services.AddControllers().AddOData();
+builder.Services.AddProblemDetails();
builder.Services.AddApiVersioning()
.AddOData(
options =>
diff --git a/examples/AspNetCore/OData/ODataConventionsExample/Configuration/OrderModelConfiguration.cs b/examples/AspNetCore/OData/ODataConventionsExample/Configuration/OrderModelConfiguration.cs
index 26573aec..c757921a 100644
--- a/examples/AspNetCore/OData/ODataConventionsExample/Configuration/OrderModelConfiguration.cs
+++ b/examples/AspNetCore/OData/ODataConventionsExample/Configuration/OrderModelConfiguration.cs
@@ -9,7 +9,7 @@ public class OrderModelConfiguration : IModelConfiguration
{
private static readonly ApiVersion V1 = new( 1, 0 );
- private EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder )
+ private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder )
{
var order = builder.EntitySet( "Orders" ).EntityType;
diff --git a/examples/AspNetCore/OData/ODataConventionsExample/Configuration/PersonModelConfiguration.cs b/examples/AspNetCore/OData/ODataConventionsExample/Configuration/PersonModelConfiguration.cs
index a0d2c5f8..5ebaa0db 100644
--- a/examples/AspNetCore/OData/ODataConventionsExample/Configuration/PersonModelConfiguration.cs
+++ b/examples/AspNetCore/OData/ODataConventionsExample/Configuration/PersonModelConfiguration.cs
@@ -7,16 +7,16 @@
public class PersonModelConfiguration : IModelConfiguration
{
- private void ConfigureV1( ODataModelBuilder builder )
+ private static void ConfigureV1( ODataModelBuilder builder )
{
var person = ConfigureCurrent( builder );
person.Ignore( p => p.Email );
person.Ignore( p => p.Phone );
}
- private void ConfigureV2( ODataModelBuilder builder ) => ConfigureCurrent( builder ).Ignore( p => p.Phone );
+ private static void ConfigureV2( ODataModelBuilder builder ) => ConfigureCurrent( builder ).Ignore( p => p.Phone );
- private EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder )
+ private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder )
{
var person = builder.EntitySet( "People" ).EntityType;
diff --git a/examples/AspNetCore/OData/ODataConventionsExample/ODataConventionsExample.csproj b/examples/AspNetCore/OData/ODataConventionsExample/ODataConventionsExample.csproj
index f0d91cbe..ecccb3a9 100644
--- a/examples/AspNetCore/OData/ODataConventionsExample/ODataConventionsExample.csproj
+++ b/examples/AspNetCore/OData/ODataConventionsExample/ODataConventionsExample.csproj
@@ -1,9 +1,5 @@
-
- net7.0
-
-
diff --git a/examples/AspNetCore/OData/ODataConventionsExample/Program.cs b/examples/AspNetCore/OData/ODataConventionsExample/Program.cs
index 2b65536a..9f937fda 100644
--- a/examples/AspNetCore/OData/ODataConventionsExample/Program.cs
+++ b/examples/AspNetCore/OData/ODataConventionsExample/Program.cs
@@ -7,6 +7,7 @@
// Add services to the container.
builder.Services.AddControllers().AddOData();
+builder.Services.AddProblemDetails();
builder.Services.AddApiVersioning(
options =>
{
diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/PersonModelConfiguration.cs b/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/PersonModelConfiguration.cs
index 5bae1fe3..98d8b060 100644
--- a/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/PersonModelConfiguration.cs
+++ b/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/PersonModelConfiguration.cs
@@ -18,6 +18,7 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string rout
person.HasKey( p => p.Id );
person.Select().OrderBy( "firstName", "lastName" );
+ person.Page( maxTopValue: 100, pageSizeValue: default );
if ( apiVersion < ApiVersions.V3 )
{
diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/ProductConfiguration.cs b/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/ProductConfiguration.cs
index e8936359..88f7d83a 100644
--- a/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/ProductConfiguration.cs
+++ b/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/ProductConfiguration.cs
@@ -18,6 +18,9 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string rout
return;
}
- var product = builder.EntitySet( "Products" ).EntityType.HasKey( p => p.Id );
+ var product = builder.EntitySet( "Products" ).EntityType;
+
+ product.HasKey( p => p.Id );
+ product.Page( maxTopValue: 100, pageSizeValue: default );
}
}
\ No newline at end of file
diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/SupplierConfiguration.cs b/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/SupplierConfiguration.cs
index 7a803487..e8f1f9e0 100644
--- a/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/SupplierConfiguration.cs
+++ b/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/SupplierConfiguration.cs
@@ -18,7 +18,12 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string rout
return;
}
- builder.EntitySet( "Suppliers" ).EntityType.HasKey( p => p.Id );
+ var supplier = builder.EntitySet( "Suppliers" ).EntityType;
+
+
+ supplier.HasKey( p => p.Id );
+ supplier.Page( maxTopValue: 100, pageSizeValue: default );
+
builder.Singleton( "Acme" );
}
}
\ No newline at end of file
diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs
index d0575a5e..5384c160 100644
--- a/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs
+++ b/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs
@@ -1,9 +1,9 @@
namespace ApiVersioning.Examples;
-using Asp.Versioning;
using Asp.Versioning.ApiExplorer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
+using Microsoft.Extensions.Primitives;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Text;
@@ -50,9 +50,9 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri
text.Append( " This API version has been deprecated." );
}
- if ( description.SunsetPolicy is SunsetPolicy policy )
+ if ( description.SunsetPolicy is { } policy )
{
- if ( policy.Date is DateTimeOffset when )
+ if ( policy.Date is { } when )
{
text.Append( " The API will be sunset on " )
.Append( when.Date.ToShortDateString() )
@@ -63,25 +63,39 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri
{
text.AppendLine();
+ var rendered = false;
+
for ( var i = 0; i < policy.Links.Count; i++ )
{
var link = policy.Links[i];
if ( link.Type == "text/html" )
{
- text.AppendLine();
-
- if ( link.Title.HasValue )
+ if ( !rendered )
{
- text.Append( link.Title.Value ).Append( ": " );
+ text.Append( "Links " );
+ }
}
}
+ text.Append( "Additional Information " );
info.Description = text.ToString();
return info;
diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/Models/Order.cs b/examples/AspNetCore/OData/ODataOpenApiExample/Models/Order.cs
index 009e9862..1df9452e 100644
--- a/examples/AspNetCore/OData/ODataOpenApiExample/Models/Order.cs
+++ b/examples/AspNetCore/OData/ODataOpenApiExample/Models/Order.cs
@@ -6,6 +6,7 @@
///
/// Represents an order.
///
+[Page( MaxTop = 100 )]
[Select]
[Select( "effectiveDate", SelectType = SelectExpandType.Disabled )]
public class Order
diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/ODataOpenApiExample.csproj b/examples/AspNetCore/OData/ODataOpenApiExample/ODataOpenApiExample.csproj
index 79a6a4b8..3bffcf5e 100644
--- a/examples/AspNetCore/OData/ODataOpenApiExample/ODataOpenApiExample.csproj
+++ b/examples/AspNetCore/OData/ODataOpenApiExample/ODataOpenApiExample.csproj
@@ -1,16 +1,15 @@
- net7.0
true
-
+
-
+
\ No newline at end of file
diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs b/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs
index 2aeeb5f0..4dd5e683 100644
--- a/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs
+++ b/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs
@@ -23,6 +23,7 @@
options.RouteOptions.EnableQualifiedOperationCall = false;
options.RouteOptions.EnableUnqualifiedOperationCall = true;
} );
+builder.Services.AddProblemDetails();
builder.Services.AddApiVersioning(
options =>
{
@@ -79,15 +80,17 @@
var app = builder.Build();
-// Configure the HTTP request pipeline.
+// Configure HTTP request pipeline.
if ( app.Environment.IsDevelopment() )
{
- // navigate to ~/$odata to determine whether any endpoints did not match an odata route template
+ // Access ~/$odata to identify OData endpoints that failed to match a route template.
app.UseODataRouteDebug();
}
app.UseSwagger();
+if ( app.Environment.IsDevelopment() )
+{
app.UseSwaggerUI(
options =>
{
@@ -101,6 +104,7 @@
options.SwaggerEndpoint( url, name );
}
} );
+}
app.UseHttpsRedirection();
app.UseAuthorization();
diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/SwaggerDefaultValues.cs b/examples/AspNetCore/OData/ODataOpenApiExample/SwaggerDefaultValues.cs
index 34369a0f..fbf06c2f 100644
--- a/examples/AspNetCore/OData/ODataOpenApiExample/SwaggerDefaultValues.cs
+++ b/examples/AspNetCore/OData/ODataOpenApiExample/SwaggerDefaultValues.cs
@@ -50,10 +50,7 @@ public void Apply( OpenApiOperation operation, OperationFilterContext context )
{
var description = apiDescription.ParameterDescriptions.First( p => p.Name == parameter.Name );
- if ( parameter.Description == null )
- {
- parameter.Description = description.ModelMetadata?.Description;
- }
+ parameter.Description ??= description.ModelMetadata?.Description;
if ( parameter.Schema.Default == null && description.DefaultValue != null )
{
diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs b/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs
index 401e1a59..652b4d4e 100644
--- a/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs
+++ b/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs
@@ -17,9 +17,9 @@
public class SuppliersController : ODataController
{
private readonly IQueryable suppliers = new[]
- {
- NewSupplier( 1 ),
- NewSupplier( 2 ),
+ {
+ NewSupplier( 1 ),
+ NewSupplier( 2 ),
NewSupplier( 3 ),
}.AsQueryable();
@@ -46,7 +46,7 @@ public class SuppliersController : ODataController
[Produces( "application/json" )]
[ProducesResponseType( typeof( Supplier ), Status200OK )]
[ProducesResponseType( Status404NotFound )]
- public SingleResult Get( int key ) =>
+ public SingleResult Get( int key ) =>
SingleResult.Create( suppliers.Where( p => p.Id == key ) );
///
@@ -147,7 +147,7 @@ public IActionResult Put( int key, [FromBody] Supplier update )
/// The supplier identifier.
/// The associated supplier products.
[HttpGet]
- [EnableQuery]
+ [EnableQuery( MaxTop = 100 )]
public IQueryable GetProducts( int key ) =>
suppliers.Where( s => s.Id == key ).SelectMany( s => s.Products );
@@ -181,8 +181,8 @@ public IActionResult CreateRef(
[ProducesResponseType( Status204NoContent )]
[ProducesResponseType( Status404NotFound )]
public IActionResult DeleteRef(
- int key,
- int relatedKey,
+ int key,
+ int relatedKey,
string navigationProperty ) => NoContent();
private static Supplier NewSupplier( int id ) =>
diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/Book.cs b/examples/AspNetCore/OData/SomeODataOpenApiExample/Book.cs
index bd9fae58..c54dcba5 100644
--- a/examples/AspNetCore/OData/SomeODataOpenApiExample/Book.cs
+++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/Book.cs
@@ -1,8 +1,15 @@
namespace ApiVersioning.Examples;
+using Microsoft.OData.ModelBuilder;
+
+// TODO: Model Bound settings can be performed via attributes if the
+// return type is known to the API Explorer or can be explicitly done
+// via one or more IModelConfiguration implementations
+
///
/// Represents a book.
///
+[Filter( "author", "published" )]
public class Book
{
///
diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/BooksController.cs b/examples/AspNetCore/OData/SomeODataOpenApiExample/BooksController.cs
index 6266bc41..68e97255 100644
--- a/examples/AspNetCore/OData/SomeODataOpenApiExample/BooksController.cs
+++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/BooksController.cs
@@ -13,15 +13,15 @@
[Route( "api/[controller]" )]
public class BooksController : ControllerBase
{
- private static readonly Book[] books = new Book[]
- {
+ private static readonly Book[] books =
+ [
new() { Id = "9781847490599", Title = "Anna Karenina", Author = "Leo Tolstoy", Published = 1878 },
new() { Id = "9780198800545", Title = "War and Peace", Author = "Leo Tolstoy", Published = 1869 },
new() { Id = "9780684801520", Title = "The Great Gatsby", Author = "F. Scott Fitzgerald", Published = 1925 },
new() { Id = "9780486280615", Title = "The Adventures of Huckleberry Finn", Author = "Mark Twain", Published = 1884 },
new() { Id = "9780140430820", Title = "Moby Dick", Author = "Herman Melville", Published = 1851 },
new() { Id = "9780060934347", Title = "Don Quixote", Author = "Miguel de Cervantes", Published = 1605 },
- };
+ ];
///
/// Gets all books.
diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs
index 987145a1..b725ab72 100644
--- a/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs
+++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs
@@ -1,9 +1,9 @@
namespace ApiVersioning.Examples;
-using Asp.Versioning;
using Asp.Versioning.ApiExplorer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
+using Microsoft.Extensions.Primitives;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Text;
@@ -50,9 +50,9 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri
text.Append( " This API version has been deprecated." );
}
- if ( description.SunsetPolicy is SunsetPolicy policy )
+ if ( description.SunsetPolicy is { } policy )
{
- if ( policy.Date is DateTimeOffset when )
+ if ( policy.Date is { } when )
{
text.Append( " The API will be sunset on " )
.Append( when.Date.ToShortDateString() )
@@ -63,25 +63,39 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri
{
text.AppendLine();
+ var rendered = false;
+
for ( var i = 0; i < policy.Links.Count; i++ )
{
var link = policy.Links[i];
if ( link.Type == "text/html" )
{
- text.AppendLine();
-
- if ( link.Title.HasValue )
+ if ( !rendered )
{
- text.Append( link.Title.Value ).Append( ": " );
+ text.Append( "Links " );
+ }
}
}
+ text.Append( "Additional Information " );
info.Description = text.ToString();
return info;
diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs b/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs
index a20f2ae0..55e70b17 100644
--- a/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs
+++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs
@@ -27,6 +27,7 @@
builder.Services.AddControllers()
.AddOData( options => options.Select() );
+builder.Services.AddProblemDetails();
builder.Services.AddApiVersioning()
.AddODataApiExplorer(
options =>
@@ -63,20 +64,22 @@
// Configure the HTTP request pipeline.
app.UseSwagger();
-app.UseSwaggerUI(
+if ( app.Environment.IsDevelopment() )
+{
+ app.UseSwaggerUI(
options =>
- {
- var descriptions = app.DescribeApiVersions();
-
- // build a swagger endpoint for each discovered API version
- foreach ( var description in descriptions )
- {
- var url = $"/swagger/{description.GroupName}/swagger.json";
- var name = description.GroupName.ToUpperInvariant();
- options.SwaggerEndpoint( url, name );
- }
- } );
-
+ {
+ var descriptions = app.DescribeApiVersions();
+
+ // build a swagger endpoint for each discovered API version
+ foreach ( var description in descriptions )
+ {
+ var url = $"/swagger/{description.GroupName}/swagger.json";
+ var name = description.GroupName.ToUpperInvariant();
+ options.SwaggerEndpoint( url, name );
+ }
+ } );
+}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/SomeODataOpenApiExample.csproj b/examples/AspNetCore/OData/SomeODataOpenApiExample/SomeODataOpenApiExample.csproj
index 79a6a4b8..3bffcf5e 100644
--- a/examples/AspNetCore/OData/SomeODataOpenApiExample/SomeODataOpenApiExample.csproj
+++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/SomeODataOpenApiExample.csproj
@@ -1,16 +1,15 @@
- net7.0
true
-
+
-
+
\ No newline at end of file
diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/SwaggerDefaultValues.cs b/examples/AspNetCore/OData/SomeODataOpenApiExample/SwaggerDefaultValues.cs
index 34369a0f..fbf06c2f 100644
--- a/examples/AspNetCore/OData/SomeODataOpenApiExample/SwaggerDefaultValues.cs
+++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/SwaggerDefaultValues.cs
@@ -50,10 +50,7 @@ public void Apply( OpenApiOperation operation, OperationFilterContext context )
{
var description = apiDescription.ParameterDescriptions.First( p => p.Name == parameter.Name );
- if ( parameter.Description == null )
- {
- parameter.Description = description.ModelMetadata?.Description;
- }
+ parameter.Description ??= description.ModelMetadata?.Description;
if ( parameter.Schema.Default == null && description.DefaultValue != null )
{
diff --git a/examples/AspNetCore/WebApi/BasicExample/BasicExample.csproj b/examples/AspNetCore/WebApi/BasicExample/BasicExample.csproj
index 24772e6a..f283af35 100644
--- a/examples/AspNetCore/WebApi/BasicExample/BasicExample.csproj
+++ b/examples/AspNetCore/WebApi/BasicExample/BasicExample.csproj
@@ -1,9 +1,5 @@
-
- net7.0
-
-
diff --git a/examples/AspNetCore/WebApi/BasicExample/Program.cs b/examples/AspNetCore/WebApi/BasicExample/Program.cs
index 8e6c97ca..79d79eda 100644
--- a/examples/AspNetCore/WebApi/BasicExample/Program.cs
+++ b/examples/AspNetCore/WebApi/BasicExample/Program.cs
@@ -5,6 +5,7 @@
// Add services to the container.
builder.Services.AddControllers();
+builder.Services.AddProblemDetails();
builder.Services.AddApiVersioning(
options =>
{
diff --git a/examples/AspNetCore/WebApi/ByNamespaceExample/ByNamespaceExample.csproj b/examples/AspNetCore/WebApi/ByNamespaceExample/ByNamespaceExample.csproj
index 24772e6a..f283af35 100644
--- a/examples/AspNetCore/WebApi/ByNamespaceExample/ByNamespaceExample.csproj
+++ b/examples/AspNetCore/WebApi/ByNamespaceExample/ByNamespaceExample.csproj
@@ -1,9 +1,5 @@
-
- net7.0
-
-
diff --git a/examples/AspNetCore/WebApi/ByNamespaceExample/Program.cs b/examples/AspNetCore/WebApi/ByNamespaceExample/Program.cs
index ab09b7fc..68a223c4 100644
--- a/examples/AspNetCore/WebApi/ByNamespaceExample/Program.cs
+++ b/examples/AspNetCore/WebApi/ByNamespaceExample/Program.cs
@@ -7,6 +7,7 @@
// Add services to the container.
builder.Services.AddControllers();
+builder.Services.AddProblemDetails();
builder.Services.AddApiVersioning(
options =>
{
diff --git a/examples/AspNetCore/WebApi/ConventionsExample/ConventionsExample.csproj b/examples/AspNetCore/WebApi/ConventionsExample/ConventionsExample.csproj
index 24772e6a..f283af35 100644
--- a/examples/AspNetCore/WebApi/ConventionsExample/ConventionsExample.csproj
+++ b/examples/AspNetCore/WebApi/ConventionsExample/ConventionsExample.csproj
@@ -1,9 +1,5 @@
-
- net7.0
-
-
diff --git a/examples/AspNetCore/WebApi/ConventionsExample/Program.cs b/examples/AspNetCore/WebApi/ConventionsExample/Program.cs
index 388989cd..756c678e 100644
--- a/examples/AspNetCore/WebApi/ConventionsExample/Program.cs
+++ b/examples/AspNetCore/WebApi/ConventionsExample/Program.cs
@@ -8,6 +8,7 @@
// Add services to the container.
builder.Services.AddControllers();
+builder.Services.AddProblemDetails();
builder.Services.AddApiVersioning(
options =>
{
diff --git a/examples/AspNetCore/WebApi/MinimalApiExample/MinimalApiExample.csproj b/examples/AspNetCore/WebApi/MinimalApiExample/MinimalApiExample.csproj
index ad99ee2f..3fc9fc03 100644
--- a/examples/AspNetCore/WebApi/MinimalApiExample/MinimalApiExample.csproj
+++ b/examples/AspNetCore/WebApi/MinimalApiExample/MinimalApiExample.csproj
@@ -1,7 +1,6 @@
- net7.0
enable
diff --git a/examples/AspNetCore/WebApi/MinimalApiExample/Program.cs b/examples/AspNetCore/WebApi/MinimalApiExample/Program.cs
index 10aa2033..4282c9f5 100644
--- a/examples/AspNetCore/WebApi/MinimalApiExample/Program.cs
+++ b/examples/AspNetCore/WebApi/MinimalApiExample/Program.cs
@@ -1,9 +1,9 @@
-using Asp.Versioning.Conventions;
-
var builder = WebApplication.CreateBuilder( args );
// Add services to the container.
+builder.Services.AddProblemDetails();
+
// enable api versioning and return the headers
// "api-supported-versions" and "api-deprecated-versions"
builder.Services.AddApiVersioning( options => options.ReportApiVersions = true );
@@ -17,10 +17,10 @@
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
-var forecast = app.MapGroup( "/weatherforecast" ).WithApiVersionSet();
+var forecast = app.NewVersionedApi();
// GET /weatherforecast?api-version=1.0
-forecast.MapGet( "/", () =>
+forecast.MapGet( "/weatherforecast", () =>
{
return Enumerable.Range( 1, 5 ).Select( index =>
new WeatherForecast
@@ -33,24 +33,25 @@
.HasApiVersion( 1.0 );
// GET /weatherforecast?api-version=2.0
-forecast.MapGet( "/", () =>
- {
- return Enumerable.Range( 0, summaries.Length ).Select( index =>
- new WeatherForecast
- (
- DateTime.Now.AddDays( index ),
- Random.Shared.Next( -20, 55 ),
- summaries[Random.Shared.Next( summaries.Length )]
- ) );
- } )
- .HasApiVersion( 2.0 );
+var v2 = forecast.MapGroup( "/weatherforecast" )
+ .HasApiVersion( 2.0 );
+
+v2.MapGet( "/", () =>
+ {
+ return Enumerable.Range( 0, summaries.Length ).Select( index =>
+ new WeatherForecast
+ (
+ DateTime.Now.AddDays( index ),
+ Random.Shared.Next( -20, 55 ),
+ summaries[Random.Shared.Next( summaries.Length )]
+ ) );
+ } );
// POST /weatherforecast?api-version=2.0
-forecast.MapPost( "/", ( WeatherForecast forecast ) => { } )
- .HasApiVersion( 2.0 );
+v2.MapPost( "/", ( WeatherForecast forecast ) => Results.Ok() );
// DELETE /weatherforecast
-forecast.MapDelete( "/", () => Results.NoContent() )
+forecast.MapDelete( "/weatherforecast", () => Results.NoContent() )
.IsApiVersionNeutral();
app.Run();
diff --git a/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs
index bee2b1f6..d531cea4 100644
--- a/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs
+++ b/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs
@@ -1,9 +1,9 @@
namespace ApiVersioning.Examples;
-using Asp.Versioning;
using Asp.Versioning.ApiExplorer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
+using Microsoft.Extensions.Primitives;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Text;
@@ -50,9 +50,9 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri
text.Append( " This API version has been deprecated." );
}
- if ( description.SunsetPolicy is SunsetPolicy policy )
+ if ( description.SunsetPolicy is { } policy )
{
- if ( policy.Date is DateTimeOffset when )
+ if ( policy.Date is { } when )
{
text.Append( " The API will be sunset on " )
.Append( when.Date.ToShortDateString() )
@@ -63,25 +63,39 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri
{
text.AppendLine();
+ var rendered = false;
+
for ( var i = 0; i < policy.Links.Count; i++ )
{
var link = policy.Links[i];
if ( link.Type == "text/html" )
{
- text.AppendLine();
-
- if ( link.Title.HasValue )
+ if ( !rendered )
{
- text.Append( link.Title.Value ).Append( ": " );
+ text.Append( "Links " );
+ }
}
}
+ text.Append( "Additional Information " );
info.Description = text.ToString();
return info;
diff --git a/examples/AspNetCore/WebApi/MinimalOpenApiExample/MinimalOpenApiExample.csproj b/examples/AspNetCore/WebApi/MinimalOpenApiExample/MinimalOpenApiExample.csproj
index 47611fa4..d2daf6ff 100644
--- a/examples/AspNetCore/WebApi/MinimalOpenApiExample/MinimalOpenApiExample.csproj
+++ b/examples/AspNetCore/WebApi/MinimalOpenApiExample/MinimalOpenApiExample.csproj
@@ -1,11 +1,7 @@
-
- net7.0
-
-
-
+
diff --git a/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs b/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs
index 7895b722..909d8261 100644
--- a/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs
+++ b/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs
@@ -1,6 +1,5 @@
using ApiVersioning.Examples;
using Asp.Versioning;
-using Asp.Versioning.Conventions;
using Microsoft.Extensions.Options;
using Swashbuckle.AspNetCore.SwaggerGen;
using OrderV1 = ApiVersioning.Examples.Models.V1.Order;
@@ -14,6 +13,7 @@
var services = builder.Services;
// Add services to the container.
+services.AddProblemDetails();
services.AddEndpointsApiExplorer();
services.AddApiVersioning(
options =>
@@ -47,231 +47,237 @@
// Configure the HTTP request pipeline.
var app = builder.Build();
-var orders = app.MapGroup( "/api/orders" ).WithApiVersionSet( "Orders" );
-var people = app.MapGroup( "/api/v{version:apiVersion}/people" ).WithApiVersionSet( "People" );
+var orders = app.NewVersionedApi( "Orders" );
+var people = app.NewVersionedApi( "People" );
// 1.0
-orders.MapGet( "/{id:int}", ( int id ) => new OrderV1() { Id = id, Customer = "John Doe" } )
- .Produces()
- .Produces( 404 )
- .HasDeprecatedApiVersion( 0.9 )
- .HasApiVersion( 1.0 );
+var ordersV1 = orders.MapGroup( "/api/orders" )
+ .HasDeprecatedApiVersion( 0.9 )
+ .HasApiVersion( 1.0 );
-orders.MapPost( "/", ( HttpRequest request, OrderV1 order ) =>
- {
- order.Id = 42;
- var scheme = request.Scheme;
- var host = request.Host;
- var location = new Uri( $"{scheme}{Uri.SchemeDelimiter}{host}/api/orders/{order.Id}" );
- return Results.Created( location, order );
- } )
- .Accepts( "application/json" )
- .Produces( 201 )
- .Produces( 400 )
- .HasApiVersion( 1.0 );
+ordersV1.MapGet( "/{id:int}", ( int id ) => new OrderV1() { Id = id, Customer = "John Doe" } )
+ .Produces()
+ .Produces( 404 );
-orders.MapMethods( "/{id:int}", new[] { HttpMethod.Patch.Method }, ( int id, OrderV1 order ) => Results.NoContent() )
- .Accepts( "application/json" )
- .Produces( 204 )
- .Produces( 400 )
- .Produces( 404 )
- .HasApiVersion( 1.0 );
+ordersV1.MapPost( "/", ( HttpRequest request, OrderV1 order ) =>
+ {
+ order.Id = 42;
+ var scheme = request.Scheme;
+ var host = request.Host;
+ var location = new Uri( $"{scheme}{Uri.SchemeDelimiter}{host}/api/orders/{order.Id}" );
+ return Results.Created( location, order );
+ } )
+ .Accepts( "application/json" )
+ .Produces( 201 )
+ .Produces( 400 )
+ .MapToApiVersion( 1.0 );
+
+ordersV1.MapPatch( "/{id:int}", ( int id, OrderV1 order ) => Results.NoContent() )
+ .Accepts( "application/json" )
+ .Produces( 204 )
+ .Produces( 400 )
+ .Produces( 404 )
+ .MapToApiVersion( 1.0 );
// 2.0
-orders.MapGet( "/", () =>
- new OrderV2[]
- {
- new(){ Id = 1, Customer = "John Doe" },
- new(){ Id = 2, Customer = "Bob Smith" },
- new(){ Id = 3, Customer = "Jane Doe", EffectiveDate = DateTimeOffset.UtcNow.AddDays( 7d ) },
- } )
- .Produces>()
- .Produces( 404 )
- .HasApiVersion( 2.0 );
+var ordersV2 = orders.MapGroup( "/api/orders" )
+ .HasApiVersion( 2.0 );
+
+ordersV2.MapGet( "/", () =>
+ new OrderV2[]
+ {
+ new(){ Id = 1, Customer = "John Doe" },
+ new(){ Id = 2, Customer = "Bob Smith" },
+ new(){ Id = 3, Customer = "Jane Doe", EffectiveDate = DateTimeOffset.UtcNow.AddDays( 7d ) },
+ } )
+ .Produces>()
+ .Produces( 404 );
+
+ordersV2.MapGet( "/{id:int}", ( int id ) => new OrderV2() { Id = id, Customer = "John Doe" } )
+ .Produces()
+ .Produces( 404 );
-orders.MapGet( "/{id:int}", ( int id ) => new OrderV2() { Id = id, Customer = "John Doe" } )
- .Produces()
- .Produces( 404 )
- .HasApiVersion( 2.0 );
+ordersV2.MapPost( "/", ( HttpRequest request, OrderV2 order ) =>
+ {
+ order.Id = 42;
+ var scheme = request.Scheme;
+ var host = request.Host;
+ var location = new Uri( $"{scheme}{Uri.SchemeDelimiter}{host}/api/orders/{order.Id}" );
+ return Results.Created( location, order );
+ } )
+ .Accepts( "application/json" )
+ .Produces( 201 )
+ .Produces( 400 );
-orders.MapPost( "/", ( HttpRequest request, OrderV2 order ) =>
- {
- order.Id = 42;
- var scheme = request.Scheme;
- var host = request.Host;
- var location = new Uri( $"{scheme}{Uri.SchemeDelimiter}{host}/api/orders/{order.Id}" );
- return Results.Created( location, order );
- } )
- .Accepts( "application/json" )
- .Produces( 201 )
- .Produces( 400 )
- .HasApiVersion( 2.0 );
-orders.MapMethods( "/{id:int}", new[] { HttpMethod.Patch.Method }, ( int id, OrderV2 order ) => Results.NoContent() )
- .Accepts( "application/json" )
- .Produces( 204 )
- .Produces( 400 )
- .Produces( 404 )
- .HasApiVersion( 2.0 );
+ordersV2.MapPatch( "/{id:int}", ( int id, OrderV2 order ) => Results.NoContent() )
+ .Accepts( "application/json" )
+ .Produces( 204 )
+ .Produces( 400 )
+ .Produces( 404 );
// 3.0
-orders.MapGet( "/", () =>
- new OrderV3[]
- {
- new(){ Id = 1, Customer = "John Doe" },
- new(){ Id = 2, Customer = "Bob Smith" },
- new(){ Id = 3, Customer = "Jane Doe", EffectiveDate = DateTimeOffset.UtcNow.AddDays( 7d ) },
- } )
- .Produces>()
- .HasApiVersion( 3.0 );
+var ordersV3 = orders.MapGroup( "/api/orders" )
+ .HasApiVersion( 3.0 );
-orders.MapGet( "/{id:int}", ( int id ) => new OrderV3() { Id = id, Customer = "John Doe" } )
- .Produces()
- .Produces( 404 )
- .HasApiVersion( 3.0 );
+ordersV3.MapGet( "/", () =>
+ new OrderV3[]
+ {
+ new(){ Id = 1, Customer = "John Doe" },
+ new(){ Id = 2, Customer = "Bob Smith" },
+ new(){ Id = 3, Customer = "Jane Doe", EffectiveDate = DateTimeOffset.UtcNow.AddDays( 7d ) },
+ } )
+ .Produces>();
-orders.MapPost( "/", ( HttpRequest request, OrderV3 order ) =>
- {
- order.Id = 42;
- var scheme = request.Scheme;
- var host = request.Host;
- var location = new Uri( $"{scheme}{Uri.SchemeDelimiter}{host}/api/orders/{order.Id}" );
- return Results.Created( location, order );
- } )
- .Accepts( "application/json" )
- .Produces( 201 )
- .Produces( 400 )
- .HasApiVersion( 3.0 );
+ordersV3.MapGet( "/{id:int}", ( int id ) => new OrderV3() { Id = id, Customer = "John Doe" } )
+ .Produces()
+ .Produces( 404 );
-orders.MapDelete( "/{id:int}", ( int id ) => Results.NoContent() )
- .Produces( 204 )
- .HasApiVersion( 3.0 );
+ordersV3.MapPost( "/", ( HttpRequest request, OrderV3 order ) =>
+ {
+ order.Id = 42;
+ var scheme = request.Scheme;
+ var host = request.Host;
+ var location = new Uri( $"{scheme}{Uri.SchemeDelimiter}{host}/api/orders/{order.Id}" );
+ return Results.Created( location, order );
+ } )
+ .Accepts( "application/json" )
+ .Produces( 201 )
+ .Produces( 400 );
+
+ordersV3.MapDelete( "/{id:int}", ( int id ) => Results.NoContent() )
+ .Produces( 204 );
// 1.0
-people.MapGet( "/{id:int}", ( int id ) =>
- new PersonV1()
- {
- Id = id,
- FirstName = "John",
- LastName = "Doe",
- } )
- .Produces()
- .Produces( 404 )
- .HasDeprecatedApiVersion( 0.9 )
- .HasApiVersion( 1.0 );
+var peopleV1 = people.MapGroup( "/api/v{version:apiVersion}/people" )
+ .HasDeprecatedApiVersion( 0.9 )
+ .HasApiVersion( 1.0 );
+
+peopleV1.MapGet( "/{id:int}", ( int id ) =>
+ new PersonV1()
+ {
+ Id = id,
+ FirstName = "John",
+ LastName = "Doe",
+ } )
+ .Produces()
+ .Produces( 404 );
// 2.0
-people.MapGet( "/", () =>
- new PersonV2[]
- {
- new()
- {
- Id = 1,
- FirstName = "John",
- LastName = "Doe",
- Email = "john.doe@somewhere.com",
- },
- new()
- {
- Id = 2,
- FirstName = "Bob",
- LastName = "Smith",
- Email = "bob.smith@somewhere.com",
- },
- new()
- {
- Id = 3,
- FirstName = "Jane",
- LastName = "Doe",
- Email = "jane.doe@somewhere.com",
- },
- } )
- .Produces>()
- .HasApiVersion( 2.0 );
+var peopleV2 = people.MapGroup( "/api/v{version:apiVersion}/people" )
+ .HasApiVersion( 2.0 );
-people.MapGet( "/{id:int}", ( int id ) =>
- new PersonV2()
- {
- Id = id,
- FirstName = "John",
- LastName = "Doe",
- Email = "john.doe@somewhere.com",
- } )
- .Produces()
- .Produces( 404 )
- .HasApiVersion( 2.0 );
+peopleV2.MapGet( "/", () =>
+ new PersonV2[]
+ {
+ new()
+ {
+ Id = 1,
+ FirstName = "John",
+ LastName = "Doe",
+ Email = "john.doe@somewhere.com",
+ },
+ new()
+ {
+ Id = 2,
+ FirstName = "Bob",
+ LastName = "Smith",
+ Email = "bob.smith@somewhere.com",
+ },
+ new()
+ {
+ Id = 3,
+ FirstName = "Jane",
+ LastName = "Doe",
+ Email = "jane.doe@somewhere.com",
+ },
+ } )
+ .Produces>();
+
+peopleV2.MapGet( "/{id:int}", ( int id ) =>
+ new PersonV2()
+ {
+ Id = id,
+ FirstName = "John",
+ LastName = "Doe",
+ Email = "john.doe@somewhere.com",
+ } )
+ .Produces()
+ .Produces( 404 );
// 3.0
-people.MapGet( "/", () =>
- new PersonV3[]
- {
- new()
- {
- Id = 1,
- FirstName = "John",
- LastName = "Doe",
- Email = "john.doe@somewhere.com",
- Phone = "555-987-1234",
- },
- new()
- {
- Id = 2,
- FirstName = "Bob",
- LastName = "Smith",
- Email = "bob.smith@somewhere.com",
- Phone = "555-654-4321",
- },
- new()
- {
- Id = 3,
- FirstName = "Jane",
- LastName = "Doe",
- Email = "jane.doe@somewhere.com",
- Phone = "555-789-3456",
- },
- } )
- .Produces>()
- .HasApiVersion( 3.0 );
+var peopleV3 = people.MapGroup( "/api/v{version:apiVersion}/people" )
+ .HasApiVersion( 3.0 );
-people.MapGet( "/{id:int}", ( int id ) =>
- new PersonV3()
- {
- Id = id,
- FirstName = "John",
- LastName = "Doe",
- Email = "john.doe@somewhere.com",
- Phone = "555-987-1234",
- } )
- .Produces()
- .Produces( 404 )
- .HasApiVersion( 3.0 );
+peopleV3.MapGet( "/", () =>
+ new PersonV3[]
+ {
+ new()
+ {
+ Id = 1,
+ FirstName = "John",
+ LastName = "Doe",
+ Email = "john.doe@somewhere.com",
+ Phone = "555-987-1234",
+ },
+ new()
+ {
+ Id = 2,
+ FirstName = "Bob",
+ LastName = "Smith",
+ Email = "bob.smith@somewhere.com",
+ Phone = "555-654-4321",
+ },
+ new()
+ {
+ Id = 3,
+ FirstName = "Jane",
+ LastName = "Doe",
+ Email = "jane.doe@somewhere.com",
+ Phone = "555-789-3456",
+ },
+ } )
+ .Produces>();
-people.MapPost( "/", ( HttpRequest request, ApiVersion version, PersonV3 person ) =>
- {
- person.Id = 42;
- var scheme = request.Scheme;
- var host = request.Host;
- var location = new Uri( $"{scheme}{Uri.SchemeDelimiter}{host}/v{version}/api/people/{person.Id}" );
- return Results.Created( location, person );
- } )
- .Accepts( "application/json" )
- .Produces( 201 )
- .Produces( 400 )
- .HasApiVersion( 3.0 );
+peopleV3.MapGet( "/{id:int}", ( int id ) =>
+ new PersonV3()
+ {
+ Id = id,
+ FirstName = "John",
+ LastName = "Doe",
+ Email = "john.doe@somewhere.com",
+ Phone = "555-987-1234",
+ } )
+ .Produces()
+ .Produces( 404 );
-app.UseSwagger();
-app.UseSwaggerUI(
- options =>
- {
- var descriptions = app.DescribeApiVersions();
+peopleV3.MapPost( "/", ( HttpRequest request, ApiVersion version, PersonV3 person ) =>
+ {
+ person.Id = 42;
+ var scheme = request.Scheme;
+ var host = request.Host;
+ var location = new Uri( $"{scheme}{Uri.SchemeDelimiter}{host}/v{version}/api/people/{person.Id}" );
+ return Results.Created( location, person );
+ } )
+ .Accepts( "application/json" )
+ .Produces( 201 )
+ .Produces( 400 );
- // build a swagger endpoint for each discovered API version
- foreach ( var description in descriptions )
+app.UseSwagger();
+if ( app.Environment.IsDevelopment() )
+{
+ app.UseSwaggerUI(
+ options =>
{
- var url = $"/swagger/{description.GroupName}/swagger.json";
- var name = description.GroupName.ToUpperInvariant();
- options.SwaggerEndpoint( url, name );
- }
- } );
+ var descriptions = app.DescribeApiVersions();
+ // build a swagger endpoint for each discovered API version
+ foreach ( var description in descriptions )
+ {
+ var url = $"/swagger/{description.GroupName}/swagger.json";
+ var name = description.GroupName.ToUpperInvariant();
+ options.SwaggerEndpoint( url, name );
+ }
+ } );
+}
app.Run();
\ No newline at end of file
diff --git a/examples/AspNetCore/WebApi/MinimalOpenApiExample/SwaggerDefaultValues.cs b/examples/AspNetCore/WebApi/MinimalOpenApiExample/SwaggerDefaultValues.cs
index 796cdd9d..0bcae0a8 100644
--- a/examples/AspNetCore/WebApi/MinimalOpenApiExample/SwaggerDefaultValues.cs
+++ b/examples/AspNetCore/WebApi/MinimalOpenApiExample/SwaggerDefaultValues.cs
@@ -47,10 +47,7 @@ public void Apply( OpenApiOperation operation, OperationFilterContext context )
{
var description = apiDescription.ParameterDescriptions.First( p => p.Name == parameter.Name );
- if ( parameter.Description == null )
- {
- parameter.Description = description.ModelMetadata?.Description;
- }
+ parameter.Description ??= description.ModelMetadata?.Description;
if ( parameter.Schema.Default == null &&
description.DefaultValue != null &&
diff --git a/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs
index bee2b1f6..d531cea4 100644
--- a/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs
+++ b/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs
@@ -1,9 +1,9 @@
namespace ApiVersioning.Examples;
-using Asp.Versioning;
using Asp.Versioning.ApiExplorer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
+using Microsoft.Extensions.Primitives;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Text;
@@ -50,9 +50,9 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri
text.Append( " This API version has been deprecated." );
}
- if ( description.SunsetPolicy is SunsetPolicy policy )
+ if ( description.SunsetPolicy is { } policy )
{
- if ( policy.Date is DateTimeOffset when )
+ if ( policy.Date is { } when )
{
text.Append( " The API will be sunset on " )
.Append( when.Date.ToShortDateString() )
@@ -63,25 +63,39 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri
{
text.AppendLine();
+ var rendered = false;
+
for ( var i = 0; i < policy.Links.Count; i++ )
{
var link = policy.Links[i];
if ( link.Type == "text/html" )
{
- text.AppendLine();
-
- if ( link.Title.HasValue )
+ if ( !rendered )
{
- text.Append( link.Title.Value ).Append( ": " );
+ text.Append( "Links " );
+ }
}
}
+ text.Append( "Additional Information " );
info.Description = text.ToString();
return info;
diff --git a/examples/AspNetCore/WebApi/OpenApiExample/OpenApiExample.csproj b/examples/AspNetCore/WebApi/OpenApiExample/OpenApiExample.csproj
index 5c6ed4ea..2be84679 100644
--- a/examples/AspNetCore/WebApi/OpenApiExample/OpenApiExample.csproj
+++ b/examples/AspNetCore/WebApi/OpenApiExample/OpenApiExample.csproj
@@ -1,12 +1,11 @@
- net7.0
true
-
+
diff --git a/examples/AspNetCore/WebApi/OpenApiExample/Program.cs b/examples/AspNetCore/WebApi/OpenApiExample/Program.cs
index 74bab524..454c60d7 100644
--- a/examples/AspNetCore/WebApi/OpenApiExample/Program.cs
+++ b/examples/AspNetCore/WebApi/OpenApiExample/Program.cs
@@ -10,6 +10,7 @@
// Add services to the container.
builder.Services.AddControllers();
+builder.Services.AddProblemDetails();
builder.Services.AddApiVersioning(
options =>
{
@@ -56,19 +57,22 @@
// Configure the HTTP request pipeline.
app.UseSwagger();
-app.UseSwaggerUI(
- options =>
- {
- var descriptions = app.DescribeApiVersions();
-
- // build a swagger endpoint for each discovered API version
- foreach ( var description in descriptions )
- {
- var url = $"/swagger/{description.GroupName}/swagger.json";
- var name = description.GroupName.ToUpperInvariant();
- options.SwaggerEndpoint( url, name );
- }
- } );
+if ( app.Environment.IsDevelopment() )
+{
+ app.UseSwaggerUI(
+ options =>
+ {
+ var descriptions = app.DescribeApiVersions();
+
+ // build a swagger endpoint for each discovered API version
+ foreach ( var description in descriptions )
+ {
+ var url = $"/swagger/{description.GroupName}/swagger.json";
+ var name = description.GroupName.ToUpperInvariant();
+ options.SwaggerEndpoint( url, name );
+ }
+ } );
+}
app.UseHttpsRedirection();
app.UseAuthorization();
diff --git a/examples/AspNetCore/WebApi/OpenApiExample/SwaggerDefaultValues.cs b/examples/AspNetCore/WebApi/OpenApiExample/SwaggerDefaultValues.cs
index 796cdd9d..0bcae0a8 100644
--- a/examples/AspNetCore/WebApi/OpenApiExample/SwaggerDefaultValues.cs
+++ b/examples/AspNetCore/WebApi/OpenApiExample/SwaggerDefaultValues.cs
@@ -47,10 +47,7 @@ public void Apply( OpenApiOperation operation, OperationFilterContext context )
{
var description = apiDescription.ParameterDescriptions.First( p => p.Name == parameter.Name );
- if ( parameter.Description == null )
- {
- parameter.Description = description.ModelMetadata?.Description;
- }
+ parameter.Description ??= description.ModelMetadata?.Description;
if ( parameter.Schema.Default == null &&
description.DefaultValue != null &&
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs
index eed5d62c..ca183af6 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs
@@ -1,12 +1,14 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+#pragma warning disable IDE0079
+#pragma warning disable CA1019
+#pragma warning disable CA1033
+#pragma warning disable CA1813
+
namespace Asp.Versioning;
using static System.AttributeTargets;
-#pragma warning disable CA1019
-#pragma warning disable CA1813
-
///
/// Represents the metadata that describes the advertised API versions .
///
@@ -71,9 +73,7 @@ public AdvertiseApiVersionsAttribute( string version ) : base( version ) { }
public AdvertiseApiVersionsAttribute( string version, params string[] otherVersions )
: base( version, otherVersions ) { }
-#pragma warning disable CA1033 // Interface methods should be callable by child types
ApiVersionProviderOptions IApiVersionProvider.Options => options;
-#pragma warning restore CA1033 // Interface methods should be callable by child types
///
/// Gets or sets a value indicating whether the specified set of API versions are deprecated.
@@ -97,5 +97,5 @@ public bool Deprecated
}
///
- public override int GetHashCode() => HashCode.Combine( GetHashCode(), Deprecated );
+ public override int GetHashCode() => HashCode.Combine( base.GetHashCode(), Deprecated );
}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/AmbiguousApiVersionException.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/AmbiguousApiVersionException.cs
index 24577d7a..4a780ad5 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/AmbiguousApiVersionException.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/AmbiguousApiVersionException.cs
@@ -12,14 +12,14 @@ public partial class AmbiguousApiVersionException : Exception
///
/// Initializes a new instance of the class.
///
- public AmbiguousApiVersionException() => apiVersions = Array.Empty();
+ public AmbiguousApiVersionException() => apiVersions = [];
///
/// Initializes a new instance of the class.
///
/// The associated error message.
public AmbiguousApiVersionException( string message )
- : base( message ) => apiVersions = Array.Empty();
+ : base( message ) => apiVersions = [];
///
/// Initializes a new instance of the class.
@@ -27,7 +27,7 @@ public AmbiguousApiVersionException( string message )
/// The associated error message.
/// The inner exception that caused the current exception, if any.
public AmbiguousApiVersionException( string message, Exception innerException )
- : base( message, innerException ) => apiVersions = Array.Empty();
+ : base( message, innerException ) => apiVersions = [];
///
/// Initializes a new instance of the class.
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersion.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersion.cs
index 3edda7f0..1bf4eeaa 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersion.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersion.cs
@@ -74,7 +74,7 @@ protected ApiVersion( double version, string? status, Func isVali
Status = ValidateStatus(
status,
- isValidStatus ?? throw new ArgumentNullException( nameof( isValidStatus ) ) );
+ isValidStatus ?? throw new System.ArgumentNullException( nameof( isValidStatus ) ) );
var number = new decimal( version );
var bits = decimal.GetBits( number );
@@ -124,10 +124,7 @@ protected internal ApiVersion(
/// The instance to derive from.
protected ApiVersion( ApiVersion other )
{
- if ( other == null )
- {
- throw new ArgumentNullException( nameof( other ) );
- }
+ ArgumentNullException.ThrowIfNull( other );
hashCode = other.hashCode;
GroupVersion = other.GroupVersion;
@@ -341,9 +338,11 @@ public virtual int CompareTo( ApiVersion? other )
public virtual string ToString( string? format, IFormatProvider? formatProvider )
{
var provider = ApiVersionFormatProvider.GetInstance( formatProvider );
+#pragma warning disable IDE0079
#pragma warning disable CA1062 // Validate arguments of public methods
return provider.Format( format, this, formatProvider );
#pragma warning restore CA1062 // Validate arguments of public methods
+#pragma warning restore IDE0079
}
private static string? ValidateStatus( string? status, Func isValid )
@@ -353,7 +352,7 @@ public virtual string ToString( string? format, IFormatProvider? formatProvider
return status;
}
- var message = string.Format( CultureInfo.CurrentCulture, SR.ApiVersionBadStatus, status );
+ var message = string.Format( CultureInfo.CurrentCulture, Format.ApiVersionBadStatus, status );
throw new ArgumentException( message, nameof( status ) );
}
}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionAttribute.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionAttribute.cs
index 2d56db41..90657500 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionAttribute.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionAttribute.cs
@@ -1,12 +1,14 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+#pragma warning disable IDE0079
+#pragma warning disable CA1019
+#pragma warning disable CA1033
+#pragma warning disable CA1813
+
namespace Asp.Versioning;
using static System.AttributeTargets;
-#pragma warning disable CA1019
-#pragma warning disable CA1813
-
///
/// Represents the metadata that describes the versions associated with an API.
///
@@ -42,9 +44,7 @@ protected ApiVersionAttribute( IApiVersionParser parser, string version ) : base
/// The API version string.
public ApiVersionAttribute( string version ) : base( version ) { }
-#pragma warning disable CA1033 // Interface methods should be callable by child types
ApiVersionProviderOptions IApiVersionProvider.Options => options;
-#pragma warning restore CA1033 // Interface methods should be callable by child types
///
/// Gets or sets a value indicating whether the specified set of API versions are deprecated.
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionFormatProvider.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionFormatProvider.cs
index 92e472ab..6ade14b6 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionFormatProvider.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionFormatProvider.cs
@@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
#pragma warning disable IDE0079
-#pragma warning disable SA1121 // Use built-in type alias
+#pragma warning disable SA1121
namespace Asp.Versioning;
@@ -200,7 +200,7 @@ public ApiVersionFormatProvider()
///
/// The used by the format provider.
public ApiVersionFormatProvider( DateTimeFormatInfo dateTimeFormat )
- : this( dateTimeFormat ?? throw new ArgumentNullException( nameof( dateTimeFormat ) ), dateTimeFormat.Calendar ) { }
+ : this( dateTimeFormat ?? throw new System.ArgumentNullException( nameof( dateTimeFormat ) ), dateTimeFormat.Calendar ) { }
///
/// Initializes a new instance of the class.
@@ -293,20 +293,11 @@ protected virtual void FormatVersionPart(
Text format,
IFormatProvider formatProvider )
{
- if ( text == null )
- {
- throw new ArgumentNullException( nameof( text ) );
- }
-
- if ( apiVersion == null )
- {
- throw new ArgumentNullException( nameof( apiVersion ) );
- }
-
- if ( Str.IsNullOrEmpty( format ) )
- {
- throw new ArgumentNullException( nameof( format ) );
- }
+ ArgumentNullException.ThrowIfNull( text );
+ ArgumentNullException.ThrowIfNull( apiVersion );
+#if NETSTANDARD1_0
+ ArgumentNullException.ThrowIfNull( format );
+#endif
switch ( format[0] )
{
@@ -334,16 +325,8 @@ protected virtual void FormatStatusPart(
Text format,
IFormatProvider formatProvider )
{
- if ( text == null )
- {
- throw new ArgumentNullException( nameof( text ) );
- }
-
- if ( apiVersion == null )
- {
- throw new ArgumentNullException( nameof( apiVersion ) );
- }
-
+ ArgumentNullException.ThrowIfNull( text );
+ ArgumentNullException.ThrowIfNull( apiVersion );
text.Append( apiVersion.Status );
}
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionMetadata.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionMetadata.cs
index 4211a665..d7e49d28 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionMetadata.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionMetadata.cs
@@ -34,10 +34,7 @@ public class ApiVersionMetadata
/// The other instance to initialize from.
protected ApiVersionMetadata( ApiVersionMetadata other )
{
- if ( other == null )
- {
- throw new ArgumentNullException( nameof( other ) );
- }
+ ArgumentNullException.ThrowIfNull( other );
apiModel = other.apiModel;
endpointModel = other.endpointModel;
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelDebugView.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelDebugView.cs
index 4d0c3ddb..00cccc5b 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelDebugView.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelDebugView.cs
@@ -2,16 +2,11 @@
namespace Asp.Versioning;
-#pragma warning disable CA1812
-
using static System.String;
-internal sealed class ApiVersionModelDebugView
+internal sealed class ApiVersionModelDebugView( ApiVersionModel model )
{
private const string Comma = ", ";
- private readonly ApiVersionModel model;
-
- public ApiVersionModelDebugView( ApiVersionModel model ) => this.model = model;
public bool VersionNeutral => model.IsApiVersionNeutral;
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelExtensions.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelExtensions.cs
index e45e0f5c..efdc86f3 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelExtensions.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelExtensions.cs
@@ -17,15 +17,8 @@ public static class ApiVersionModelExtensions
/// other version information and the current version information.
public static ApiVersionModel Aggregate( this ApiVersionModel version, ApiVersionModel otherVersion )
{
- if ( version == null )
- {
- throw new ArgumentNullException( nameof( version ) );
- }
-
- if ( otherVersion == null )
- {
- throw new ArgumentNullException( nameof( otherVersion ) );
- }
+ ArgumentNullException.ThrowIfNull( version );
+ ArgumentNullException.ThrowIfNull( otherVersion );
var implemented = new SortedSet( version.ImplementedApiVersions );
var supported = new SortedSet( version.SupportedApiVersions );
@@ -50,15 +43,8 @@ public static ApiVersionModel Aggregate( this ApiVersionModel version, ApiVersio
/// other version information and the current version information.
public static ApiVersionModel Aggregate( this ApiVersionModel version, IEnumerable otherVersions )
{
- if ( version == null )
- {
- throw new ArgumentNullException( nameof( version ) );
- }
-
- if ( otherVersions == null )
- {
- throw new ArgumentNullException( nameof( otherVersions ) );
- }
+ ArgumentNullException.ThrowIfNull( version );
+ ArgumentNullException.ThrowIfNull( otherVersions );
if ( ( otherVersions is ICollection collection && collection.Count == 0 ) ||
( otherVersions is IReadOnlyCollection readOnlyCollection && readOnlyCollection.Count == 0 ) )
@@ -99,10 +85,7 @@ public static ApiVersionModel Aggregate( this ApiVersionModel version, IEnumerab
/// A new that is the aggregated result of the provided version information .
public static ApiVersionModel Aggregate( this IEnumerable versions )
{
- if ( versions == null )
- {
- throw new ArgumentNullException( nameof( versions ) );
- }
+ ArgumentNullException.ThrowIfNull( versions );
if ( ( versions is ICollection collection && collection.Count == 0 ) ||
( versions is IReadOnlyCollection readOnlyCollection && readOnlyCollection.Count == 0 ) )
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs
index 8bd12675..97ec10bb 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs
@@ -2,7 +2,6 @@
#pragma warning disable IDE0079
#pragma warning disable SA1121
-#pragma warning disable SA1114 // Parameter list should follow declaration
namespace Asp.Versioning;
@@ -188,8 +187,6 @@ public virtual ApiVersion Parse( Text text )
///
#if NETSTANDARD1_0
public virtual bool TryParse( Text? text, out ApiVersion apiVersion )
-#elif NETSTANDARD2_0
- public virtual bool TryParse( Text text, out ApiVersion apiVersion )
#else
public virtual bool TryParse( Text text, [MaybeNullWhen( false )] out ApiVersion apiVersion )
#endif
@@ -339,11 +336,11 @@ public virtual bool TryParse( Text text, [MaybeNullWhen( false )] out ApiVersion
[MethodImpl( MethodImplOptions.AggressiveInlining )]
private static FormatException InvalidGroupVersion( string value ) =>
- new( string.Format( CultureInfo.CurrentCulture, SR.ApiVersionBadGroupVersion, value ) );
+ new( string.Format( CultureInfo.CurrentCulture, Format.ApiVersionBadGroupVersion, value ) );
[MethodImpl( MethodImplOptions.AggressiveInlining )]
private static FormatException InvalidStatus( string value ) =>
- new( string.Format( CultureInfo.CurrentCulture, SR.ApiVersionBadStatus, value ) );
+ new( string.Format( CultureInfo.CurrentCulture, Format.ApiVersionBadStatus, value ) );
private static bool IsDateLike( Text value )
{
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionsBaseAttribute.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionsBaseAttribute.cs
index 212e20ac..4c966ae5 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionsBaseAttribute.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionsBaseAttribute.cs
@@ -96,7 +96,7 @@ protected ApiVersionsBaseAttribute( string version, params string[] otherVersion
/// The parser used to parse the specified versions.
/// The API version string.
protected ApiVersionsBaseAttribute( IApiVersionParser parser, string version ) =>
- Versions = new[] { ( parser ?? throw new ArgumentNullException( nameof( parser ) ) ).Parse( version ) };
+ Versions = new[] { ( parser ?? throw new System.ArgumentNullException( nameof( parser ) ) ).Parse( version ) };
///
/// Initializes a new instance of the class.
@@ -106,10 +106,7 @@ protected ApiVersionsBaseAttribute( IApiVersionParser parser, string version ) =
/// An array of API other version strings.
protected ApiVersionsBaseAttribute( IApiVersionParser parser, string version, params string[] otherVersions )
{
- if ( parser == null )
- {
- throw new ArgumentNullException( nameof( parser ) );
- }
+ ArgumentNullException.ThrowIfNull( parser );
int count;
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj b/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj
index 13cf5936..68dd7991 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj
@@ -1,18 +1,22 @@
-
+
- 7.0.0
- 7.0.0.0
- net7.0;netstandard1.0;netstandard2.0
+ 8.1.0
+ 8.1.0.0
+ $(DefaultTargetFramework);netstandard1.0;netstandard2.0
API Versioning Abstractions
The abstractions library for API versioning.
Asp.Versioning
Asp;AspNet;AspNetCore;Versioning
+
+ true
+
+
-
-
+
+
@@ -22,21 +26,28 @@
-
-
+
+
-
+
+
+
+
+
+
+
+
-
+
-
+
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/Conventions/ApiVersionConventionBuilderBase.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/Conventions/ApiVersionConventionBuilderBase.cs
index 4a431637..4ebe62bd 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/Conventions/ApiVersionConventionBuilderBase.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/Conventions/ApiVersionConventionBuilderBase.cs
@@ -39,25 +39,25 @@ protected ApiVersionConventionBuilderBase() { }
/// Gets the collection of API versions supported by the current controller.
///
/// A collection of supported API versions .
- protected ICollection SupportedVersions => supported ??= new();
+ protected ICollection SupportedVersions => supported ??= [];
///
/// Gets the collection of API versions deprecated by the current controller.
///
/// A collection of deprecated API versions .
- protected ICollection DeprecatedVersions => deprecated ??= new();
+ protected ICollection DeprecatedVersions => deprecated ??= [];
///
/// Gets the collection of API versions advertised by the current controller.
///
/// A collection of advertised API versions .
- protected ICollection AdvertisedVersions => advertised ??= new();
+ protected ICollection AdvertisedVersions => advertised ??= [];
///
/// Gets the collection of API versions advertised and deprecated by the current controller.
///
/// A collection of advertised and deprecated API versions .
- protected ICollection DeprecatedAdvertisedVersions => deprecatedAdvertised ??= new();
+ protected ICollection DeprecatedAdvertisedVersions => deprecatedAdvertised ??= [];
///
/// Merges API version information from the specified attributes with the current conventions.
@@ -72,10 +72,7 @@ protected virtual void MergeAttributesWithConventions( IEnumerable attri
/// The read-only list of attributes to merge.
protected virtual void MergeAttributesWithConventions( IReadOnlyList attributes )
{
- if ( attributes == null )
- {
- throw new ArgumentNullException( nameof( attributes ) );
- }
+ ArgumentNullException.ThrowIfNull( attributes );
if ( VersionNeutral )
{
@@ -102,19 +99,19 @@ protected virtual void MergeAttributesWithConventions( IReadOnlyList att
switch ( provider.Options )
{
case None:
- target = newSupported ??= new();
+ target = newSupported ??= [];
source = provider.Versions;
break;
case Deprecated:
- target = newDeprecated ??= new();
+ target = newDeprecated ??= [];
source = provider.Versions;
break;
case Advertised:
- target = newAdvertised ??= new();
+ target = newAdvertised ??= [];
source = provider.Versions;
break;
case DeprecatedAdvertised:
- target = newDeprecatedAdvertised ??= new();
+ target = newDeprecatedAdvertised ??= [];
source = provider.Versions;
break;
default:
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/Conventions/ApiVersionConventionBuilderExtensions.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/Conventions/ApiVersionConventionBuilderExtensions.cs
index 3813f478..9d528546 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/Conventions/ApiVersionConventionBuilderExtensions.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/Conventions/ApiVersionConventionBuilderExtensions.cs
@@ -84,10 +84,7 @@ public static class ApiVersionConventionBuilderExtensions
public static T HasApiVersions( this T builder, IEnumerable apiVersions )
where T : notnull, IDeclareApiVersionConventionBuilder
{
- if ( apiVersions == null )
- {
- throw new ArgumentNullException( nameof( apiVersions ) );
- }
+ ArgumentNullException.ThrowIfNull( apiVersions );
foreach ( var apiVersion in apiVersions )
{
@@ -170,10 +167,7 @@ public static T HasApiVersions( this T builder, IEnumerable apiVe
public static T HasDeprecatedApiVersions( this T builder, IEnumerable apiVersions )
where T : notnull, IDeclareApiVersionConventionBuilder
{
- if ( apiVersions == null )
- {
- throw new ArgumentNullException( nameof( apiVersions ) );
- }
+ ArgumentNullException.ThrowIfNull( apiVersions );
foreach ( var apiVersion in apiVersions )
{
@@ -256,10 +250,7 @@ public static T HasDeprecatedApiVersions( this T builder, IEnumerable( this T builder, IEnumerable apiVersions )
where T : notnull, IDeclareApiVersionConventionBuilder
{
- if ( apiVersions == null )
- {
- throw new ArgumentNullException( nameof( apiVersions ) );
- }
+ ArgumentNullException.ThrowIfNull( apiVersions );
foreach ( var apiVersion in apiVersions )
{
@@ -342,10 +333,7 @@ public static T AdvertisesApiVersions( this T builder, IEnumerable( this T builder, IEnumerable apiVersions )
where T : notnull, IDeclareApiVersionConventionBuilder
{
- if ( apiVersions == null )
- {
- throw new ArgumentNullException( nameof( apiVersions ) );
- }
+ ArgumentNullException.ThrowIfNull( apiVersions );
foreach ( var apiVersion in apiVersions )
{
@@ -428,10 +416,7 @@ public static T AdvertisesDeprecatedApiVersions( this T builder, IEnumerable<
public static T MapToApiVersions( this T builder, IEnumerable apiVersions )
where T : notnull, IMapToApiVersionConventionBuilder
{
- if ( apiVersions == null )
- {
- throw new ArgumentNullException( nameof( apiVersions ) );
- }
+ ArgumentNullException.ThrowIfNull( apiVersions );
foreach ( var apiVersion in apiVersions )
{
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/Format.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/Format.cs
new file mode 100644
index 00000000..dbea4469
--- /dev/null
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/Format.cs
@@ -0,0 +1,18 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+#if NET
+using System.Text;
+#endif
+
+internal static class Format
+{
+#if NETSTANDARD
+ internal static readonly string ApiVersionBadStatus = SR.ApiVersionBadStatus;
+ internal static readonly string ApiVersionBadGroupVersion = SR.ApiVersionBadGroupVersion;
+#else
+ internal static readonly CompositeFormat ApiVersionBadStatus = CompositeFormat.Parse( SR.ApiVersionBadStatus );
+ internal static readonly CompositeFormat ApiVersionBadGroupVersion = CompositeFormat.Parse( SR.ApiVersionBadGroupVersion );
+#endif
+}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionNeutral.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionNeutral.cs
index a3ba5fd2..375541ef 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionNeutral.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionNeutral.cs
@@ -1,8 +1,10 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-namespace Asp.Versioning;
+#pragma warning disable IDE0079
#pragma warning disable CA1040
+namespace Asp.Versioning;
+
///
/// Defines the behavior of an API that is version-neutral.
///
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionParameterSourceExtensions.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionParameterSourceExtensions.cs
index 4a3dd194..55022bce 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionParameterSourceExtensions.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionParameterSourceExtensions.cs
@@ -18,10 +18,7 @@ public static class IApiVersionParameterSourceExtensions
/// True if the parameter source versions by query string; otherwise, false.
public static bool VersionsByQueryString( this IApiVersionParameterSource source, bool allowMultipleLocations = true )
{
- if ( source == null )
- {
- throw new ArgumentNullException( nameof( source ) );
- }
+ ArgumentNullException.ThrowIfNull( source );
var context = new DescriptionContext( Query );
@@ -39,10 +36,7 @@ public static bool VersionsByQueryString( this IApiVersionParameterSource source
/// True if the parameter source versions by HTTP header; otherwise, false.
public static bool VersionsByHeader( this IApiVersionParameterSource source, bool allowMultipleLocations = true )
{
- if ( source == null )
- {
- throw new ArgumentNullException( nameof( source ) );
- }
+ ArgumentNullException.ThrowIfNull( source );
var context = new DescriptionContext( Header );
@@ -60,10 +54,7 @@ public static bool VersionsByHeader( this IApiVersionParameterSource source, boo
/// True if the parameter source versions by URL path segment; otherwise, false.
public static bool VersionsByUrl( this IApiVersionParameterSource source, bool allowMultipleLocations = true )
{
- if ( source == null )
- {
- throw new ArgumentNullException( nameof( source ) );
- }
+ ArgumentNullException.ThrowIfNull( source );
var context = new DescriptionContext( Path );
@@ -81,10 +72,7 @@ public static bool VersionsByUrl( this IApiVersionParameterSource source, bool a
/// True if the parameter source versions by media type; otherwise, false.
public static bool VersionsByMediaType( this IApiVersionParameterSource source, bool allowMultipleLocations = true )
{
- if ( source == null )
- {
- throw new ArgumentNullException( nameof( source ) );
- }
+ ArgumentNullException.ThrowIfNull( source );
var context = new DescriptionContext( MediaTypeParameter );
@@ -102,10 +90,7 @@ public static bool VersionsByMediaType( this IApiVersionParameterSource source,
/// or null .
public static string GetParameterName( this IApiVersionParameterSource source, ApiVersionParameterLocation location )
{
- if ( source == null )
- {
- throw new ArgumentNullException( nameof( source ) );
- }
+ ArgumentNullException.ThrowIfNull( source );
var context = new DescriptionContext( location );
@@ -122,10 +107,7 @@ public static string GetParameterName( this IApiVersionParameterSource source, A
/// The names of the parameters defined by the parameter source for the specified .
public static IReadOnlyList GetParameterNames( this IApiVersionParameterSource source, ApiVersionParameterLocation location )
{
- if ( source == null )
- {
- throw new ArgumentNullException( nameof( source ) );
- }
+ ArgumentNullException.ThrowIfNull( source );
var context = new DescriptionContext( location );
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersioningPolicyBuilderExtensions.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersioningPolicyBuilderExtensions.cs
index e2040ba2..048d6d49 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersioningPolicyBuilderExtensions.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersioningPolicyBuilderExtensions.cs
@@ -19,11 +19,7 @@ public static class IApiVersioningPolicyBuilderExtensions
/// A new sunset policy builder .
public static ISunsetPolicyBuilder Sunset( this IApiVersioningPolicyBuilder builder, string name )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
return builder.Sunset( name, default );
}
@@ -43,11 +39,7 @@ public static ISunsetPolicyBuilder Sunset(
int? minorVersion = default,
string? status = default )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
return builder.Sunset( name, new ApiVersion( majorVersion, minorVersion, status ) );
}
@@ -61,11 +53,7 @@ public static ISunsetPolicyBuilder Sunset(
/// A new sunset policy builder .
public static ISunsetPolicyBuilder Sunset( this IApiVersioningPolicyBuilder builder, string name, double version, string? status = default )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
return builder.Sunset( name, new ApiVersion( version, status ) );
}
@@ -81,11 +69,7 @@ public static ISunsetPolicyBuilder Sunset(
/// A new sunset policy builder .
public static ISunsetPolicyBuilder Sunset( this IApiVersioningPolicyBuilder builder, string name, int year, int month, int day, string? status = default )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
return builder.Sunset( name, new ApiVersion( new DateOnly( year, month, day ), status ) );
}
@@ -99,11 +83,7 @@ public static ISunsetPolicyBuilder Sunset(
/// A new sunset policy builder .
public static ISunsetPolicyBuilder Sunset( this IApiVersioningPolicyBuilder builder, string name, DateOnly groupVersion, string? status = default )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
return builder.Sunset( name, new ApiVersion( groupVersion, status ) );
}
@@ -115,11 +95,7 @@ public static ISunsetPolicyBuilder Sunset(
/// A new sunset policy builder .
public static ISunsetPolicyBuilder Sunset( this IApiVersioningPolicyBuilder builder, ApiVersion apiVersion )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
return builder.Sunset( default, apiVersion );
}
@@ -137,11 +113,7 @@ public static ISunsetPolicyBuilder Sunset(
int? minorVersion = default,
string? status = default )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
return builder.Sunset( default, new ApiVersion( majorVersion, minorVersion, status ) );
}
@@ -154,11 +126,7 @@ public static ISunsetPolicyBuilder Sunset(
/// A new sunset policy builder .
public static ISunsetPolicyBuilder Sunset( this IApiVersioningPolicyBuilder builder, double version, string? status = default )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
return builder.Sunset( default, new ApiVersion( version, status ) );
}
@@ -173,11 +141,7 @@ public static ISunsetPolicyBuilder Sunset(
/// A new sunset policy builder .
public static ISunsetPolicyBuilder Sunset( this IApiVersioningPolicyBuilder builder, int year, int month, int day, string? status = default )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
return builder.Sunset( default, new ApiVersion( new DateOnly( year, month, day ), status ) );
}
@@ -190,11 +154,7 @@ public static ISunsetPolicyBuilder Sunset(
/// A new sunset policy builder .
public static ISunsetPolicyBuilder Sunset( this IApiVersioningPolicyBuilder builder, DateOnly groupVersion, string? status = default )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
return builder.Sunset( default, new ApiVersion( groupVersion, status ) );
}
}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ILinkBuilderExtensions.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ILinkBuilderExtensions.cs
index ccb9a64e..acf444f9 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/ILinkBuilderExtensions.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ILinkBuilderExtensions.cs
@@ -15,11 +15,7 @@ public static class ILinkBuilderExtensions
/// A new link builder .
public static ILinkBuilder Link( this ILinkBuilder builder, string linkTarget )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
return builder.Link( new Uri( linkTarget, UriKind.RelativeOrAbsolute ) );
}
}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyBuilderExtensions.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyBuilderExtensions.cs
index 778f78d7..4a81ebd4 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyBuilderExtensions.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyBuilderExtensions.cs
@@ -15,11 +15,7 @@ public static class ISunsetPolicyBuilderExtensions
/// A new link builder .
public static ILinkBuilder Link( this ISunsetPolicyBuilder builder, string linkTarget )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
return builder.Link( new Uri( linkTarget, UriKind.RelativeOrAbsolute ) );
}
@@ -35,11 +31,7 @@ public static ILinkBuilder Link( this ISunsetPolicyBuilder builder, string linkT
public static TBuilder Effective( this TBuilder builder, int year, int month, int day )
where TBuilder : notnull, ISunsetPolicyBuilder
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
builder.Effective( new DateTimeOffset( new DateTime( year, month, day ) ) );
return builder;
}
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManager.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManager.cs
index 77a175a7..de2dd77e 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManager.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManager.cs
@@ -18,11 +18,5 @@ public interface ISunsetPolicyManager
/// policy for the specified API version . If
/// API version is null , it is assumed the caller intends to match
/// any sunset policy for the specified .
- bool TryGetPolicy(
- string? name,
- ApiVersion? apiVersion,
-#if !NETSTANDARD
- [MaybeNullWhen( false )]
-#endif
- out SunsetPolicy sunsetPolicy );
+ bool TryGetPolicy( string? name, ApiVersion? apiVersion, [MaybeNullWhen( false )] out SunsetPolicy sunsetPolicy );
}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManagerExtensions.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManagerExtensions.cs
index 11e21279..f28f9cca 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManagerExtensions.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManagerExtensions.cs
@@ -17,16 +17,9 @@ public static class ISunsetPolicyManagerExtensions
public static bool TryGetPolicy(
this ISunsetPolicyManager policyManager,
ApiVersion apiVersion,
-#if !NETSTANDARD
- [MaybeNullWhen( false )]
-#endif
- out SunsetPolicy sunsetPolicy )
+ [MaybeNullWhen( false )] out SunsetPolicy sunsetPolicy )
{
- if ( policyManager == null )
- {
- throw new ArgumentNullException( nameof( policyManager ) );
- }
-
+ ArgumentNullException.ThrowIfNull( policyManager );
return policyManager.TryGetPolicy( default, apiVersion, out sunsetPolicy );
}
@@ -40,16 +33,9 @@ public static bool TryGetPolicy(
public static bool TryGetPolicy(
this ISunsetPolicyManager policyManager,
string name,
-#if !NETSTANDARD
- [MaybeNullWhen( false )]
-#endif
- out SunsetPolicy sunsetPolicy )
+ [MaybeNullWhen( false )] out SunsetPolicy sunsetPolicy )
{
- if ( policyManager == null )
- {
- throw new ArgumentNullException( nameof( policyManager ) );
- }
-
+ ArgumentNullException.ThrowIfNull( policyManager );
return policyManager.TryGetPolicy( name, default, out sunsetPolicy );
}
@@ -60,7 +46,7 @@ public static bool TryGetPolicy(
/// The name of the API.
/// The API version to get the policy for.
/// The applicable sunset policy , if any.
- /// The resolution or is as follows:
+ /// The resolution order is as follows:
///
/// and
/// only
@@ -72,10 +58,7 @@ public static bool TryGetPolicy(
string? name,
ApiVersion? apiVersion )
{
- if ( policyManager == null )
- {
- throw new ArgumentNullException( nameof( policyManager ) );
- }
+ ArgumentNullException.ThrowIfNull( policyManager );
if ( policyManager.TryResolvePolicy( name, apiVersion, out var policy ) )
{
@@ -93,7 +76,7 @@ public static bool TryGetPolicy(
/// The API version to get the policy for.
/// /// The applicable sunset policy , if any.
/// True if the sunset policy was retrieved; otherwise, false.
- /// The resolution or is as follows:
+ /// The resolution order is as follows:
///
/// and
/// only
@@ -104,15 +87,9 @@ public static bool TryResolvePolicy(
this ISunsetPolicyManager policyManager,
string? name,
ApiVersion? apiVersion,
-#if !NETSTANDARD
- [MaybeNullWhen( false )]
-#endif
- out SunsetPolicy sunsetPolicy )
+ [MaybeNullWhen( false )] out SunsetPolicy sunsetPolicy )
{
- if ( policyManager == null )
- {
- throw new ArgumentNullException( nameof( policyManager ) );
- }
+ ArgumentNullException.ThrowIfNull( policyManager );
if ( !string.IsNullOrEmpty( name ) )
{
@@ -125,7 +102,8 @@ public static bool TryResolvePolicy(
return true;
}
}
- else if ( apiVersion != null && policyManager.TryGetPolicy( apiVersion, out sunsetPolicy ) )
+
+ if ( apiVersion != null && policyManager.TryGetPolicy( apiVersion, out sunsetPolicy ) )
{
return true;
}
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs
index fed3d08e..94088bc9 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs
@@ -1,5 +1,8 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+#pragma warning disable IDE0079
+#pragma warning disable SA1121
+
namespace Asp.Versioning;
#if !NETSTANDARD1_0
@@ -13,7 +16,6 @@ namespace Asp.Versioning;
#endif
#pragma warning disable IDE0079
-#pragma warning disable SA1121
///
/// Represents a HTTP Link header value.
@@ -83,7 +85,7 @@ public StringSegment Language
#endif
if ( languages is null )
{
- languages = new() { value };
+ languages = [value];
}
else if ( languages.Count == 0 )
{
@@ -103,7 +105,7 @@ public StringSegment Language
/// This is only a hint; for example, it does not override the Content-Language header field of
/// a HTTP response obtained by actually following the link. A single link may indicate that multiple
/// languages are available from the indicated resource.
- public IList Languages => languages ??= new();
+ public IList Languages => languages ??= [];
///
/// Gets or sets the link media.
@@ -159,10 +161,7 @@ public StringSegment Language
public static bool TryParse(
StringSegment input,
Func? resolveRelativeUrl,
-#if !NETSTANDARD
- [MaybeNullWhen( false )]
-#endif
- out LinkHeaderValue parsedValue )
+ [MaybeNullWhen( false )] out LinkHeaderValue parsedValue )
{
#if NETSTANDARD1_0
if ( string.IsNullOrEmpty( input ) )
@@ -207,11 +206,11 @@ public static bool TryParse(
type = attribute.Value;
break;
case "hreflang":
- languages ??= new();
+ languages ??= [];
languages.Add( attribute.Value );
break;
default:
- extensions ??= new();
+ extensions ??= [];
extensions.Add( attribute );
break;
}
@@ -300,9 +299,7 @@ public static bool TryParse(
public static bool TryParseList(
IList? input,
Func? resolveRelativeUrl,
-#if !NETSTANDARD
[MaybeNullWhen( false )]
-#endif
out IList parsedValues )
{
if ( input == null )
@@ -402,10 +399,7 @@ private static void AppendTargetAttribute( StringBuilder builder, ReadOnlySpan? resolveRelativeUrl,
-#if !NETSTANDARD
- [MaybeNullWhen( false )]
-#endif
- out Uri targetLink )
+ [MaybeNullWhen( false )] out Uri targetLink )
{
var start = segment.IndexOf( '<' );
@@ -493,7 +487,7 @@ public bool Remove( KeyValuePair item ) =>
public bool TryGetValue(
StringSegment key,
-#if !NETSTANDARD
+#if !NETSTANDARD1_0
[MaybeNullWhen( false )]
#endif
out StringSegment value ) => items.TryGetValue( key, out value );
@@ -538,16 +532,10 @@ private static ref StringSegment ValidateKey( ref StringSegment key )
}
}
- private struct TargetAttributesEnumerator : IEnumerable>
+ private struct TargetAttributesEnumerator( StringSegment remaining )
+ : IEnumerable>
{
- private readonly StringSegment remaining;
- private int start;
-
- public TargetAttributesEnumerator( StringSegment remaining )
- {
- this.remaining = remaining;
- start = 0;
- }
+ private int start = 0;
public IEnumerator> GetEnumerator()
{
@@ -570,7 +558,7 @@ public IEnumerator> GetEnumerator()
}
// REF: https://datatracker.ietf.org/doc/html/rfc8288#appendix-B.3 #9
-#pragma warning disable CA1308 // Normalize strings to uppercase
+#pragma warning disable CA1308 // Normalize strings to uppercase (all ascii and should normalize to lowercase)
#if NETSTANDARD1_0
var key = remaining.Substring( start, end - start ).ToLowerInvariant();
#else
@@ -649,10 +637,13 @@ private static StringSegment RemoveQuotes( StringSegment input )
return input;
}
+#pragma warning disable IDE0056 // Use index operator
private static bool IsQuoted( StringSegment input ) =>
-#pragma warning disable IDE0056
- !StringSegment.IsNullOrEmpty( input ) && input.Length >= 2 && input[0] == '"' && input[input.Length - 1] == '"';
-#pragma warning restore IDE0056
+ !StringSegment.IsNullOrEmpty( input ) &&
+ input.Length >= 2 &&
+ input[0] == '"' &&
+ input[input.Length - 1] == '"';
+#pragma warning restore IDE0056 // Use index operator
private static StringSegment UnescapeAsQuotedString( StringSegment input )
{
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/MapToApiVersionAttribute.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/MapToApiVersionAttribute.cs
index 35c9aeb9..e90f5c58 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/MapToApiVersionAttribute.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/MapToApiVersionAttribute.cs
@@ -1,12 +1,14 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+#pragma warning disable IDE0079
+#pragma warning disable CA1019
+#pragma warning disable CA1033
+#pragma warning disable CA1813
+
namespace Asp.Versioning;
using static System.AttributeTargets;
-#pragma warning disable CA1019
-#pragma warning disable CA1813
-
///
/// Represents the metadata that describes the version -specific implementation of an API.
///
@@ -38,7 +40,5 @@ public MapToApiVersionAttribute( double version ) : base( version ) { }
/// The API version string.
public MapToApiVersionAttribute( string version ) : base( version ) { }
-#pragma warning disable CA1033
ApiVersionProviderOptions IApiVersionProvider.Options => ApiVersionProviderOptions.Mapped;
-#pragma warning restore CA1033
}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/NamespaceParser.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/NamespaceParser.cs
index 46d64d23..a4246749 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/NamespaceParser.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/NamespaceParser.cs
@@ -1,8 +1,8 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
#pragma warning disable IDE0079
+#pragma warning disable SA1114
#pragma warning disable SA1121
-#pragma warning disable SA1114 // Parameter list should follow declaration
namespace Asp.Versioning;
@@ -53,10 +53,7 @@ public class NamespaceParser
/// A read-only list of API verisons .
public IReadOnlyList Parse( Type type )
{
- if ( type == null )
- {
- throw new ArgumentNullException( nameof( type ) );
- }
+ ArgumentNullException.ThrowIfNull( type );
if ( string.IsNullOrEmpty( type.Namespace ) )
{
@@ -95,7 +92,7 @@ public IReadOnlyList Parse( Type type )
}
else if ( versions is null )
{
- versions = new() { version, result };
+ versions = [version, result];
}
else
{
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/Str.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/Str.cs
index 850e5049..0c899a4b 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/Str.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/Str.cs
@@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
#pragma warning disable IDE0079
-#pragma warning disable SA1121 // Use built-in type alias
+#pragma warning disable SA1121
namespace Asp.Versioning;
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/net7.0/ApiVersion.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/net#.0/ApiVersion.cs
similarity index 91%
rename from src/Abstractions/src/Asp.Versioning.Abstractions/net7.0/ApiVersion.cs
rename to src/Abstractions/src/Asp.Versioning.Abstractions/net#.0/ApiVersion.cs
index 2a2db484..c9eb3b00 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/net7.0/ApiVersion.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/net#.0/ApiVersion.cs
@@ -11,8 +11,10 @@ public partial class ApiVersion : ISpanFormattable
public virtual bool TryFormat( Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider )
{
var instance = ApiVersionFormatProvider.GetInstance( provider );
+#pragma warning disable IDE0079
#pragma warning disable CA1062 // Validate arguments of public methods
return instance.TryFormat( destination, out charsWritten, format, this, provider );
#pragma warning restore CA1062 // Validate arguments of public methods
+#pragma warning restore IDE0079
}
}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard1.0/ApiVersionFormatProvider.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard1.0/ApiVersionFormatProvider.cs
index faf0e6df..2cf84bb0 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard1.0/ApiVersionFormatProvider.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard1.0/ApiVersionFormatProvider.cs
@@ -22,15 +22,8 @@ protected virtual void FormatGroupVersionPart(
string? format,
IFormatProvider? formatProvider )
{
- if ( text == null )
- {
- throw new ArgumentNullException( nameof( text ) );
- }
-
- if ( apiVersion == null )
- {
- throw new ArgumentNullException( nameof( apiVersion ) );
- }
+ ArgumentNullException.ThrowIfNull( text );
+ ArgumentNullException.ThrowIfNull( apiVersion );
if ( !apiVersion.GroupVersion.HasValue || string.IsNullOrEmpty( format ) )
{
@@ -106,15 +99,8 @@ protected virtual void FormatAllParts(
string? format,
IFormatProvider? formatProvider )
{
- if ( text == null )
- {
- throw new ArgumentNullException( nameof( text ) );
- }
-
- if ( apiVersion == null )
- {
- throw new ArgumentNullException( nameof( apiVersion ) );
- }
+ ArgumentNullException.ThrowIfNull( text );
+ ArgumentNullException.ThrowIfNull( apiVersion );
if ( apiVersion.GroupVersion.HasValue )
{
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/AmbiguousApiVersionException.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/AmbiguousApiVersionException.cs
index 7215f3e9..baf164e4 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/AmbiguousApiVersionException.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/AmbiguousApiVersionException.cs
@@ -2,6 +2,7 @@
namespace Asp.Versioning;
+using System.ComponentModel;
using System.Runtime.Serialization;
///
@@ -10,11 +11,23 @@ namespace Asp.Versioning;
[Serializable]
public partial class AmbiguousApiVersionException : Exception
{
+ private const string LegacyFormatterImplMessage = "This API supports obsolete formatter-based serialization. It should not be called or extended by application code.";
+#if NET
+ private const string LegacyFormatterImplDiagId = "SYSLIB0051";
+ private const string SharedUrlFormat = "https://aka.ms/dotnet-warnings/{0}";
+#endif
+
///
/// Initializes a new instance of the class.
///
/// The serialization info the exception is being deserialized with.
/// The streaming context the exception is being deserialized from.
+#if NET
+ [Obsolete( LegacyFormatterImplMessage, DiagnosticId = LegacyFormatterImplDiagId, UrlFormat = SharedUrlFormat )]
+#else
+ [Obsolete( LegacyFormatterImplMessage )]
+#endif
+ [EditorBrowsable( EditorBrowsableState.Never )]
protected AmbiguousApiVersionException( SerializationInfo info, StreamingContext context )
: base( info, context ) => apiVersions = (string[]) info.GetValue( nameof( apiVersions ), typeof( string[] ) )!;
@@ -23,6 +36,10 @@ protected AmbiguousApiVersionException( SerializationInfo info, StreamingContext
///
/// The serialization info the exception is being serialized with.
/// The streaming context the exception is being serialized in.
+#if NET
+ [Obsolete( LegacyFormatterImplMessage, DiagnosticId = LegacyFormatterImplDiagId, UrlFormat = SharedUrlFormat )]
+#endif
+ [EditorBrowsable( EditorBrowsableState.Never )]
public override void GetObjectData( SerializationInfo info, StreamingContext context )
{
base.GetObjectData( info, context );
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/ApiVersionFormatProvider.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/ApiVersionFormatProvider.cs
index ddf26eca..76ad58af 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/ApiVersionFormatProvider.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/ApiVersionFormatProvider.cs
@@ -354,15 +354,8 @@ protected virtual void FormatGroupVersionPart(
in ReadOnlySpan format,
IFormatProvider? formatProvider )
{
- if ( text == null )
- {
- throw new ArgumentNullException( nameof( text ) );
- }
-
- if ( apiVersion == null )
- {
- throw new ArgumentNullException( nameof( apiVersion ) );
- }
+ ArgumentNullException.ThrowIfNull( text );
+ ArgumentNullException.ThrowIfNull( apiVersion );
if ( !apiVersion.GroupVersion.HasValue || format.IsEmpty )
{
@@ -448,15 +441,8 @@ protected virtual void FormatAllParts(
in ReadOnlySpan format,
IFormatProvider? formatProvider )
{
- if ( text == null )
- {
- throw new ArgumentNullException( nameof( text ) );
- }
-
- if ( apiVersion == null )
- {
- throw new ArgumentNullException( nameof( apiVersion ) );
- }
+ ArgumentNullException.ThrowIfNull( text );
+ ArgumentNullException.ThrowIfNull( apiVersion );
Span buffer = stackalloc char[10];
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/FormatWriter.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/FormatWriter.cs
index 3fa0645f..b0c8019a 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/FormatWriter.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/FormatWriter.cs
@@ -45,7 +45,7 @@ internal FormatWriter(
public bool Succeeded { get; private set; }
- public int Written => totalWritten;
+ public readonly int Written => totalWritten;
public void Write( in FormatToken token )
{
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParser.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParser.cs
index c806e8dd..cfbf616e 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParser.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParser.cs
@@ -20,10 +20,5 @@ public interface IApiVersionParser
/// The text to parse as an API version.
/// The parsed API version or null.
/// True if the parsing was successful; otherwise false.
- bool TryParse(
- ReadOnlySpan text,
-#if !NETSTANDARD
- [MaybeNullWhen( false )]
-#endif
- out ApiVersion apiVersion );
+ bool TryParse( ReadOnlySpan text, [MaybeNullWhen( false )] out ApiVersion apiVersion );
}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParserExtensions.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParserExtensions.cs
index 1eabc3b4..3d23a71e 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParserExtensions.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParserExtensions.cs
@@ -15,11 +15,7 @@ public static class IApiVersionParserExtensions
/// The parsed API version.
public static ApiVersion Parse( this IApiVersionParser parser, string? text )
{
- if ( parser == null )
- {
- throw new ArgumentNullException( nameof( parser ) );
- }
-
+ ArgumentNullException.ThrowIfNull( parser );
return parser.Parse( text == null ? default : text.AsSpan() );
}
@@ -33,16 +29,9 @@ public static ApiVersion Parse( this IApiVersionParser parser, string? text )
public static bool TryParse(
this IApiVersionParser parser,
string? text,
-#if !NETSTANDARD
- [MaybeNullWhen( false )]
-#endif
- out ApiVersion apiVersion )
+ [MaybeNullWhen( false )] out ApiVersion apiVersion )
{
- if ( parser == null )
- {
- throw new ArgumentNullException( nameof( parser ) );
- }
-
+ ArgumentNullException.ThrowIfNull( parser );
return parser.TryParse( text == null ? default : text.AsSpan(), out apiVersion );
}
}
\ No newline at end of file
diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionFormatProviderTest.cs b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionFormatProviderTest.cs
index 64b440a8..7b8d2245 100644
--- a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionFormatProviderTest.cs
+++ b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionFormatProviderTest.cs
@@ -41,6 +41,7 @@ public void get_format_should_return_expected_format_provider()
}
[Theory]
+ [AssumeCulture( "en-us" )]
[MemberData( nameof( FormatProvidersData ) )]
public void format_should_allow_null_or_empty_format_string( ApiVersionFormatProvider provider )
{
@@ -56,6 +57,7 @@ public void format_should_allow_null_or_empty_format_string( ApiVersionFormatPro
}
[Theory]
+ [AssumeCulture( "en-us" )]
[MemberData( nameof( FormatProvidersData ) )]
public void format_should_return_full_formatted_string_without_optional_components( ApiVersionFormatProvider provider )
{
@@ -70,6 +72,7 @@ public void format_should_return_full_formatted_string_without_optional_componen
}
[Theory]
+ [AssumeCulture( "en-us" )]
[MemberData( nameof( FormatProvidersData ) )]
public void format_should_return_full_formatted_string_with_optional_components( ApiVersionFormatProvider provider )
{
@@ -84,6 +87,7 @@ public void format_should_return_full_formatted_string_with_optional_components(
}
[Theory]
+ [AssumeCulture( "en-us" )]
[MemberData( nameof( FormatProvidersData ) )]
public void format_should_return_original_string_format_when_argument_cannot_be_formatted( ApiVersionFormatProvider provider )
{
@@ -113,6 +117,7 @@ public void format_should_not_allow_malformed_literal_string( ApiVersionFormatPr
}
[Theory]
+ [AssumeCulture( "en-us" )]
[MemberData( nameof( GroupVersionFormatData ) )]
public void format_should_return_formatted_group_version_string( ApiVersionFormatProvider provider, string format )
{
@@ -129,6 +134,7 @@ public void format_should_return_formatted_group_version_string( ApiVersionForma
}
[Theory]
+ [AssumeCulture( "en-us" )]
[MemberData( nameof( FormatProvidersData ) )]
public void format_should_return_formatted_minor_version_string( ApiVersionFormatProvider provider )
{
@@ -143,6 +149,7 @@ public void format_should_return_formatted_minor_version_string( ApiVersionForma
}
[Theory]
+ [AssumeCulture( "en-us" )]
[MemberData( nameof( FormatProvidersData ) )]
public void format_should_return_formatted_major_version_string( ApiVersionFormatProvider provider )
{
@@ -157,6 +164,7 @@ public void format_should_return_formatted_major_version_string( ApiVersionForma
}
[Theory]
+ [AssumeCulture( "en-us" )]
[MemberData( nameof( FormatProvidersData ) )]
public void format_should_return_formatted_major_and_minor_version_string( ApiVersionFormatProvider provider )
{
@@ -171,6 +179,7 @@ public void format_should_return_formatted_major_and_minor_version_string( ApiVe
}
[Theory]
+ [AssumeCulture( "en-us" )]
[MemberData( nameof( FormatProvidersData ) )]
public void format_should_return_formatted_short_version_string( ApiVersionFormatProvider provider )
{
@@ -185,6 +194,7 @@ public void format_should_return_formatted_short_version_string( ApiVersionForma
}
[Theory]
+ [AssumeCulture( "en-us" )]
[MemberData( nameof( FormatProvidersData ) )]
public void format_should_return_formatted_long_version_string( ApiVersionFormatProvider provider )
{
@@ -199,6 +209,7 @@ public void format_should_return_formatted_long_version_string( ApiVersionFormat
}
[Theory]
+ [AssumeCulture( "en-us" )]
[MemberData( nameof( FormatProvidersData ) )]
public void format_should_return_formatted_status_string( ApiVersionFormatProvider provider )
{
@@ -213,6 +224,7 @@ public void format_should_return_formatted_status_string( ApiVersionFormatProvid
}
[Theory]
+ [AssumeCulture( "en-us" )]
[MemberData( nameof( PaddedMinorVersionFormatData ) )]
public void format_should_return_formatted_minor_version_with_padding_string( ApiVersionFormatProvider provider, string format )
{
@@ -233,6 +245,7 @@ public void format_should_return_formatted_minor_version_with_padding_string( Ap
}
[Theory]
+ [AssumeCulture( "en-us" )]
[MemberData( nameof( PaddedMajorVersionFormatData ) )]
public void format_should_return_formatted_major_version_with_padding_string( ApiVersionFormatProvider provider, string format )
{
@@ -253,6 +266,7 @@ public void format_should_return_formatted_major_version_with_padding_string( Ap
}
[Theory]
+ [AssumeCulture( "en-us" )]
[MemberData( nameof( CustomFormatData ) )]
public void format_should_return_custom_format_string( Func format, string expected )
{
@@ -268,6 +282,7 @@ public void format_should_return_custom_format_string( Func
}
[Theory]
+ [AssumeCulture( "en-us" )]
[MemberData( nameof( MultipleFormatParameterData ) )]
public void format_should_return_formatted_string_with_multiple_parameters( ApiVersionFormatProvider provider, string format, object secondArgument, string expected )
{
@@ -284,6 +299,7 @@ public void format_should_return_formatted_string_with_multiple_parameters( ApiV
}
[Fact]
+ [AssumeCulture( "en-us" )]
public void format_should_return_formatted_string_with_escape_sequence()
{
// arrange
diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionTest.cs b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionTest.cs
index 07186a3e..11c896a5 100644
--- a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionTest.cs
+++ b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionTest.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+//// Ignore Spelling: DX
+
namespace Asp.Versioning;
public partial class ApiVersionTest
@@ -595,27 +597,27 @@ public void api_version_1_ge_api_version_2_should_return_expected_result( string
}
public static IEnumerable FormatData =>
- new[]
+ new object[][]
{
- new[] { null, "2013-08-06.1.1-Alpha", "2013-08-06.1.1-Alpha" },
- new[] { "", "2013-08-06.1.1-Alpha", "2013-08-06.1.1-Alpha" },
- new[] { "F", "2013-08-06.1.1-Alpha", "2013-08-06.1.1-Alpha" },
- new[] { "G", "2013-08-06", "2013-08-06" },
- new[] { "GG", "2013-08-06-Alpha", "2013-08-06-Alpha" },
- new[] { "G", "1.1", "" },
- new[] { "G", "1.1-Alpha", "" },
- new[] { "G", "2013-08-06.1.1", "2013-08-06" },
- new[] { "GG", "2013-08-06.1.1-Alpha", "2013-08-06-Alpha" },
- new[] { "V", "2013-08-06", "" },
- new[] { "VVVV", "2013-08-06-Alpha", "" },
- new[] { "VV", "1.1", "1.1" },
- new[] { "VVVV", "1.1-Alpha", "1.1-Alpha" },
- new[] { "VV", "2013-08-06.1.1", "1.1" },
- new[] { "VVVV", "2013-08-06.1.1-Alpha", "1.1-Alpha" },
- new[] { "S", "1.1-Alpha", "Alpha" },
- new[] { "'v'VVV", "1.1", "v1.1" },
- new[] { "'Major': %V, 'Minor': %v", "1.1", "Major: 1, Minor: 1" },
- new[] { "MMM yyyy '('S')'", "2013-08-06-preview.1", "Aug 2013 (preview.1)" },
+ [null, "2013-08-06.1.1-Alpha", "2013-08-06.1.1-Alpha"],
+ ["", "2013-08-06.1.1-Alpha", "2013-08-06.1.1-Alpha"],
+ ["F", "2013-08-06.1.1-Alpha", "2013-08-06.1.1-Alpha"],
+ ["G", "2013-08-06", "2013-08-06"],
+ ["GG", "2013-08-06-Alpha", "2013-08-06-Alpha"],
+ ["G", "1.1", ""],
+ ["G", "1.1-Alpha", ""],
+ ["G", "2013-08-06.1.1", "2013-08-06"],
+ ["GG", "2013-08-06.1.1-Alpha", "2013-08-06-Alpha"],
+ ["V", "2013-08-06", ""],
+ ["VVVV", "2013-08-06-Alpha", ""],
+ ["VV", "1.1", "1.1"],
+ ["VVVV", "1.1-Alpha", "1.1-Alpha"],
+ ["VV", "2013-08-06.1.1", "1.1"],
+ ["VVVV", "2013-08-06.1.1-Alpha", "1.1-Alpha"],
+ ["S", "1.1-Alpha", "Alpha"],
+ ["'v'VVV", "1.1", "v1.1"],
+ ["'Major': %V, 'Minor': %v", "1.1", "Major: 1, Minor: 1"],
+ ["MMM yyyy '('S')'", "2013-08-06-preview.1", "Aug 2013 (preview.1)"],
};
#if NETFRAMEWORK
diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/Asp.Versioning.Abstractions.Tests.csproj b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/Asp.Versioning.Abstractions.Tests.csproj
index a7c04d59..e2fcdd62 100644
--- a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/Asp.Versioning.Abstractions.Tests.csproj
+++ b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/Asp.Versioning.Abstractions.Tests.csproj
@@ -1,14 +1,14 @@
- net7.0;net472;net452
+ $(DefaultTargetFramework);net452;net472
Asp.Versioning
-
-
-
+
+
+
-
+
diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/AssumeCultureAttribute.cs b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/AssumeCultureAttribute.cs
new file mode 100644
index 00000000..9c956d87
--- /dev/null
+++ b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/AssumeCultureAttribute.cs
@@ -0,0 +1,40 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+using System.Globalization;
+using System.Reflection;
+using Xunit.Sdk;
+using static System.AttributeTargets;
+using static System.Threading.Thread;
+
+///
+/// Allows a test method to assume that it is running in a specific locale.
+///
+[AttributeUsage( Class | Method, AllowMultiple = false, Inherited = true )]
+public sealed class AssumeCultureAttribute : BeforeAfterTestAttribute
+{
+ private CultureInfo originalCulture;
+ private CultureInfo originalUICulture;
+
+ public AssumeCultureAttribute( string name ) => Name = name;
+
+ public string Name { get; }
+
+ public override void Before( MethodInfo methodUnderTest )
+ {
+ originalCulture = CurrentThread.CurrentCulture;
+ originalUICulture = CurrentThread.CurrentUICulture;
+
+ var culture = CultureInfo.CreateSpecificCulture( Name );
+
+ CurrentThread.CurrentCulture = culture;
+ CurrentThread.CurrentUICulture = culture;
+ }
+
+ public override void After( MethodInfo methodUnderTest )
+ {
+ CurrentThread.CurrentCulture = originalCulture;
+ CurrentThread.CurrentUICulture = originalUICulture;
+ }
+}
\ No newline at end of file
diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/IApiVersionParameterSourceExtensionsTest.cs b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/IApiVersionParameterSourceExtensionsTest.cs
index b1186e85..486977b0 100644
--- a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/IApiVersionParameterSourceExtensionsTest.cs
+++ b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/IApiVersionParameterSourceExtensionsTest.cs
@@ -146,6 +146,7 @@ public void get_parameter_names_should_return_matching_names()
{
// arrange
var source = new Mock();
+ var expected = new[] { "api-version", "ver" };
source.Setup( s => s.AddParameters( It.IsAny() ) )
.Callback( ( IApiVersionParameterDescriptionContext context ) =>
@@ -160,6 +161,6 @@ public void get_parameter_names_should_return_matching_names()
var names = source.Object.GetParameterNames( Query );
// assert
- names.Should().BeEquivalentTo( new[] { "api-version", "ver" } );
+ names.Should().BeEquivalentTo( expected );
}
}
\ No newline at end of file
diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ISunsetPolicyManagerExtensionsTest.cs b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ISunsetPolicyManagerExtensionsTest.cs
index ac1cb2c0..9cc9b693 100644
--- a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ISunsetPolicyManagerExtensionsTest.cs
+++ b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ISunsetPolicyManagerExtensionsTest.cs
@@ -65,11 +65,10 @@ public void resolve_policy_should_fall_back_to_global_result()
var expected = new SunsetPolicy();
var other = new SunsetPolicy();
- manager.Setup( m => m.TryGetPolicy( "Test", new ApiVersion( 1.0, null ), out other ) ).Returns( true );
- manager.Setup( m => m.TryGetPolicy( default, new ApiVersion( 1.0, null ), out expected ) ).Returns( true );
+ manager.Setup( m => m.TryGetPolicy( It.IsAny(), new ApiVersion( 1.0, null ), out expected ) ).Returns( true );
// act
- var policy = manager.Object.ResolvePolicyOrDefault( default, new ApiVersion( 1.0 ) );
+ var policy = manager.Object.ResolvePolicyOrDefault( "Test", new ApiVersion( 1.0 ) );
// assert
policy.Should().BeSameAs( expected );
diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/net7.0/ApiVersionTest.cs b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/net#.0/ApiVersionTest.cs
similarity index 95%
rename from src/Abstractions/test/Asp.Versioning.Abstractions.Tests/net7.0/ApiVersionTest.cs
rename to src/Abstractions/test/Asp.Versioning.Abstractions.Tests/net#.0/ApiVersionTest.cs
index b650303b..b1b6f794 100644
--- a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/net7.0/ApiVersionTest.cs
+++ b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/net#.0/ApiVersionTest.cs
@@ -6,6 +6,7 @@ public partial class ApiVersionTest
{
[Theory]
[MemberData( nameof( FormatData ) )]
+ [AssumeCulture( "en-us" )]
public void try_format_format_should_return_expected_string( string format, string text, string formattedString )
{
// arrange
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Asp.Versioning.WebApi.Acceptance.Tests.csproj b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Asp.Versioning.WebApi.Acceptance.Tests.csproj
index 4a958c4f..29f5fbf5 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Asp.Versioning.WebApi.Acceptance.Tests.csproj
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Asp.Versioning.WebApi.Acceptance.Tests.csproj
@@ -6,8 +6,8 @@
-
-
+
+
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OrdersController.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OrdersController.cs
index 84f14bd6..d9ffd452 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OrdersController.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OrdersController.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+#pragma warning disable IDE0060
+
namespace Asp.Versioning.Http.Basic.Controllers;
using Asp.Versioning.Http.Basic.Models;
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OverlappingRouteTemplateController.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OverlappingRouteTemplateController.cs
index 320ec366..4907c19e 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OverlappingRouteTemplateController.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OverlappingRouteTemplateController.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+#pragma warning disable IDE0060
+
namespace Asp.Versioning.Http.Basic.Controllers;
using System.Web.Http;
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/InteropFixture.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/InteropFixture.cs
new file mode 100644
index 00000000..b61d1d68
--- /dev/null
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/InteropFixture.cs
@@ -0,0 +1,15 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+// Ignore Spelling: Interop
+namespace Asp.Versioning.Http.Basic;
+
+using System.Web.Http;
+
+public class InteropFixture : BasicFixture
+{
+ protected override void OnConfigure( HttpConfiguration configuration )
+ {
+ configuration.ConvertProblemDetailsToErrorObject();
+ base.OnConfigure( configuration );
+ }
+}
\ No newline at end of file
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/given a versioned ApiController/when error objects are enabled.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/given a versioned ApiController/when error objects are enabled.cs
new file mode 100644
index 00000000..b40bfa14
--- /dev/null
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/given a versioned ApiController/when error objects are enabled.cs
@@ -0,0 +1,51 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace given_a_versioned_ApiController;
+
+using Asp.Versioning;
+using Asp.Versioning.Http.Basic;
+
+public class when_error_objects_are_enabled : AcceptanceTest, IClassFixture
+{
+ [Fact]
+ public async Task then_the_response_should_not_be_problem_details()
+ {
+ // arrange
+ var example = new
+ {
+ error = new
+ {
+ code = default( string ),
+ message = default( string ),
+ target = default( string ),
+ innerError = new
+ {
+ message = default( string ),
+ },
+ },
+ };
+
+ // act
+ var response = await GetAsync( "api/values?api-version=3.0" );
+ var error = await response.Content.ReadAsExampleAsync( example );
+
+ // assert
+ response.Content.Headers.ContentType.MediaType.Should().Be( "application/json" );
+ error.Should().BeEquivalentTo(
+ new
+ {
+ error = new
+ {
+ code = "UnsupportedApiVersion",
+ message = "Unsupported API version",
+ innerError = new
+ {
+ message = "No route providing a controller name with API version '3.0' " +
+ "was found to match request URI 'http://localhost/api/values'.",
+ },
+ },
+ } );
+ }
+
+ public when_error_objects_are_enabled( InteropFixture fixture ) : base( fixture ) { }
+}
\ No newline at end of file
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/given a versioned ApiController/when using a query string and split into two types.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/given a versioned ApiController/when using a query string and split into two types.cs
index fab6d7d1..cffde314 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/given a versioned ApiController/when using a query string and split into two types.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/given a versioned ApiController/when using a query string and split into two types.cs
@@ -34,7 +34,7 @@ public async Task then_get_should_return_200( string controller, string apiVersi
}
[Fact]
- public async Task then_get_should_return_404_for_an_unsupported_version()
+ public async Task then_get_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -44,7 +44,7 @@ public async Task then_get_should_return_404_for_an_unsupported_version()
var problem = await response.Content.ReadAsProblemDetailsAsync();
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
problem.Type.Should().Be( ProblemDetailsDefaults.Unsupported.Type );
}
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingConventions/Controllers/OrdersController.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingConventions/Controllers/OrdersController.cs
index 7e6d3e27..1b52ff0d 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingConventions/Controllers/OrdersController.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingConventions/Controllers/OrdersController.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+#pragma warning disable IDE0060
+
namespace Asp.Versioning.Http.UsingConventions.Controllers;
using Asp.Versioning.Http.UsingConventions.Models;
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingConventions/given a versioned ApiController using conventions/when using a query string and split into two types.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingConventions/given a versioned ApiController using conventions/when using a query string and split into two types.cs
index 08ecf18c..1080b60e 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingConventions/given a versioned ApiController using conventions/when using a query string and split into two types.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingConventions/given a versioned ApiController using conventions/when using a query string and split into two types.cs
@@ -35,7 +35,7 @@ public async Task then_get_should_return_200( string controller, string apiVersi
}
[Fact]
- public async Task then_get_should_return_404_for_an_unsupported_version()
+ public async Task then_get_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -45,7 +45,7 @@ public async Task then_get_should_return_404_for_an_unsupported_version()
var problem = await response.Content.ReadAsProblemDetailsAsync();
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
problem.Type.Should().Be( ProblemDetailsDefaults.Unsupported.Type );
}
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/Controllers/V1/OrdersController.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/Controllers/V1/OrdersController.cs
index 8e19bda5..d547a271 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/Controllers/V1/OrdersController.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/Controllers/V1/OrdersController.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+#pragma warning disable IDE0060
+
namespace Asp.Versioning.Http.UsingNamespace.Controllers.V1;
using Asp.Versioning.Http.UsingNamespace.Models;
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a query string.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a query string.cs
index 652b9347..06c84e76 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a query string.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a query string.cs
@@ -32,7 +32,7 @@ public async Task then_get_should_return_200( Type controllerType, string apiVer
}
[Fact]
- public async Task then_get_should_return_404_for_an_unsupported_version()
+ public async Task then_get_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -42,7 +42,7 @@ public async Task then_get_should_return_404_for_an_unsupported_version()
var problem = await response.Content.ReadAsProblemDetailsAsync();
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
problem.Type.Should().Be( ProblemDetailsDefaults.Unsupported.Type );
}
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and attribute-based routing.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and attribute-based routing.cs
index 0beae68c..8cd50259 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and attribute-based routing.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and attribute-based routing.cs
@@ -2,6 +2,8 @@
#pragma warning disable IDE1006 // Naming Styles
+//// Ignore Spelling: Dbased
+
namespace given_a_versioned_ApiController_per_namespace;
using Asp.Versioning;
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and convention-based routing.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and convention-based routing.cs
index 63801fb2..88f11bf8 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and convention-based routing.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and convention-based routing.cs
@@ -2,6 +2,8 @@
#pragma warning disable IDE1006 // Naming Styles
+//// Ignore Spelling: Dbased
+
namespace given_a_versioned_ApiController_per_namespace;
using Asp.Versioning;
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Advanced/given a versioned ODataController mixed with Web API controllers/when people is any version.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Advanced/given a versioned ODataController mixed with Web API controllers/when people is any version.cs
index 9070597e..b354d6f6 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Advanced/given a versioned ODataController mixed with Web API controllers/when people is any version.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Advanced/given a versioned ODataController mixed with Web API controllers/when people is any version.cs
@@ -9,7 +9,7 @@ namespace given_a_versioned_ODataController_mixed_with_Web_API_controllers;
public class when_people_is_any_version : AdvancedAcceptanceTest
{
[Fact]
- public async Task then_patch_should_return_404_for_an_unsupported_version()
+ public async Task then_patch_should_return_400_for_an_unsupported_version()
{
// arrange
var person = new { lastName = "Me" };
@@ -19,7 +19,7 @@ public async Task then_patch_should_return_404_for_an_unsupported_version()
var problem = await response.Content.ReadAsProblemDetailsAsync();
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
problem.Type.Should().Be( ProblemDetailsDefaults.Unsupported.Type );
}
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs
index 26c45c91..3d05e49f 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
#pragma warning disable IDE0060 // Remove unused parameter
-#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.OData.Basic.Controllers;
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string and split into two types.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string and split into two types.cs
index f32b79c3..ad4f5364 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string and split into two types.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string and split into two types.cs
@@ -28,7 +28,7 @@ public async Task then_get_should_return_200( string requestUrl )
}
[Fact]
- public async Task then_get_should_return_404_for_an_unsupported_version()
+ public async Task then_get_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -38,7 +38,7 @@ public async Task then_get_should_return_404_for_an_unsupported_version()
var problem = await response.Content.ReadAsProblemDetailsAsync();
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
problem.Type.Should().Be( ProblemDetailsDefaults.Unsupported.Type );
}
@@ -73,7 +73,7 @@ public async Task then_patch_should_return_405_if_supported_in_any_version( stri
}
[Fact]
- public async Task then_patch_should_return_404_for_an_unsupported_version()
+ public async Task then_patch_should_return_400_for_an_unsupported_version()
{
// arrange
var person = new { id = 42, firstName = "John", lastName = "Doe", email = "john.doe@somewhere.com" };
@@ -83,7 +83,7 @@ public async Task then_patch_should_return_404_for_an_unsupported_version()
var problem = await response.Content.ReadAsProblemDetailsAsync();
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
problem.Type.Should().Be( ProblemDetailsDefaults.Unsupported.Type );
}
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string.cs
index 591fc75f..b73cde2c 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string.cs
@@ -24,7 +24,7 @@ public async Task then_get_should_return_200( string requestUrl )
}
[Fact]
- public async Task then_get_should_return_404_for_an_unsupported_version()
+ public async Task then_get_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -34,7 +34,7 @@ public async Task then_get_should_return_404_for_an_unsupported_version()
var problem = await response.Content.ReadAsProblemDetailsAsync();
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
problem.Type.Should().Be( ProblemDetailsDefaults.Unsupported.Type );
}
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs
index 1515dd37..27b3e2d9 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-
namespace Asp.Versioning.OData.Configuration;
using Asp.Versioning.OData.Models;
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs
index ed7eb5a7..05ec159e 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-
namespace Asp.Versioning.OData.Configuration;
using Asp.Versioning.OData.Models;
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs
index 1ebc086a..302e5c38 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-
namespace Asp.Versioning.OData.Configuration;
using Asp.Versioning.OData.Models;
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs
index 83609324..357bb455 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-
namespace Asp.Versioning.OData.Configuration;
using Asp.Versioning.OData.Models;
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/ODataAcceptanceTest.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/ODataAcceptanceTest.cs
index 32491a77..9c420a43 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/ODataAcceptanceTest.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/ODataAcceptanceTest.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+//// Ignore Spelling: Dspecific
+
namespace Asp.Versioning.OData;
using static System.Net.HttpStatusCode;
@@ -37,7 +39,7 @@ public async Task then_the_service_document_should_be_versionX2Dspecific( string
}
[Fact]
- public async Task then_the_service_document_should_return_404_for_an_unsupported_version()
+ public async Task then_the_service_document_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -47,7 +49,7 @@ public async Task then_the_service_document_should_return_404_for_an_unsupported
var problem = await response.Content.ReadAsProblemDetailsAsync();
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
problem.Type.Should().Be( ProblemDetailsDefaults.Unsupported.Type );
}
@@ -83,7 +85,7 @@ public async Task then_X24metadata_should_be_versionX2Dspecific( string apiVersi
}
[Fact]
- public async Task then_X24metadata_should_return_404_for_an_unsupported_version()
+ public async Task then_X24metadata_should_return_400_for_an_unsupported_version()
{
// arrange
Client.DefaultRequestHeaders.Clear();
@@ -93,7 +95,7 @@ public async Task then_X24metadata_should_return_404_for_an_unsupported_version(
var problem = await response.Content.ReadAsProblemDetailsAsync();
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
problem.Type.Should().Be( ProblemDetailsDefaults.Unsupported.Type );
}
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string and split into two types.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string and split into two types.cs
index 0cd5170d..46e15b8d 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string and split into two types.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string and split into two types.cs
@@ -28,7 +28,7 @@ public async Task then_get_should_return_200( string requestUrl )
}
[Fact]
- public async Task then_get_should_return_404_for_an_unsupported_version()
+ public async Task then_get_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -38,7 +38,7 @@ public async Task then_get_should_return_404_for_an_unsupported_version()
var problem = await response.Content.ReadAsProblemDetailsAsync();
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
problem.Type.Should().Be( ProblemDetailsDefaults.Unsupported.Type );
}
@@ -73,7 +73,7 @@ public async Task then_patch_should_return_405_if_supported_in_any_version( stri
}
[Fact]
- public async Task then_patch_should_return_404_for_an_unsupported_version()
+ public async Task then_patch_should_return_400_for_an_unsupported_version()
{
// arrange
var person = new { id = 42, firstName = "John", lastName = "Doe", email = "john.doe@somewhere.com" };
@@ -83,7 +83,7 @@ public async Task then_patch_should_return_404_for_an_unsupported_version()
var problem = await response.Content.ReadAsProblemDetailsAsync();
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
problem.Type.Should().Be( ProblemDetailsDefaults.Unsupported.Type );
}
diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string.cs
index ba475388..4956d346 100644
--- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string.cs
+++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string.cs
@@ -24,7 +24,7 @@ public async Task then_get_should_return_200( string requestUrl )
}
[Fact]
- public async Task then_get_should_return_404_for_an_unsupported_version()
+ public async Task then_get_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -34,7 +34,7 @@ public async Task then_get_should_return_404_for_an_unsupported_version()
var problem = await response.Content.ReadAsProblemDetailsAsync();
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
problem.Type.Should().Be( ProblemDetailsDefaults.Unsupported.Type );
}
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/AdHocEdmScope.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/AdHocEdmScope.cs
new file mode 100644
index 00000000..f6744216
--- /dev/null
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/AdHocEdmScope.cs
@@ -0,0 +1,100 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.ApiExplorer;
+
+using Asp.Versioning.Conventions;
+using Asp.Versioning.Description;
+using Asp.Versioning.OData;
+using Microsoft.OData.Edm;
+using System.Collections.Generic;
+using System.Web.Http;
+using System.Web.Http.Description;
+
+internal sealed class AdHocEdmScope : IDisposable
+{
+ private readonly IReadOnlyList results;
+ private bool disposed;
+
+ internal AdHocEdmScope(
+ IReadOnlyList apiDescriptions,
+ VersionedODataModelBuilder builder )
+ {
+ var conventions = builder.ModelConfigurations.OfType().ToArray();
+
+ results = FilterResults( apiDescriptions, conventions );
+
+ if ( results.Count > 0 )
+ {
+ ApplyAdHocEdm( builder.GetEdmModels(), results );
+ }
+ }
+
+ public void Dispose()
+ {
+ if ( disposed )
+ {
+ return;
+ }
+
+ disposed = true;
+
+ for ( var i = 0; i < results.Count; i++ )
+ {
+ results[i].SetProperty( default( IEdmModel ) );
+ }
+ }
+
+ private static IReadOnlyList FilterResults(
+ IReadOnlyList apiDescriptions,
+ IReadOnlyList conventions )
+ {
+ if ( conventions.Count == 0 )
+ {
+ return Array.Empty();
+ }
+
+ var results = default( List );
+
+ for ( var i = 0; i < apiDescriptions.Count; i++ )
+ {
+ var apiDescription = apiDescriptions[i];
+
+ if ( apiDescription.EdmModel() != null || !apiDescription.IsODataLike() )
+ {
+ continue;
+ }
+
+ results ??= [];
+ results.Add( apiDescription );
+
+ for ( var j = 0; j < conventions.Count; j++ )
+ {
+ conventions[j].ApplyTo( apiDescription );
+ }
+ }
+
+ return results?.ToArray() ?? [];
+ }
+
+ private static void ApplyAdHocEdm(
+ IReadOnlyList models,
+ IReadOnlyList results )
+ {
+ for ( var i = 0; i < models.Count; i++ )
+ {
+ var model = models[i];
+ var version = model.GetApiVersion();
+
+ for ( var j = 0; j < results.Count; j++ )
+ {
+ var result = results[j];
+ var metadata = result.ActionDescriptor.GetApiVersionMetadata();
+
+ if ( metadata.IsMappedTo( version ) )
+ {
+ result.SetProperty( model );
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs
index f6fda7f9..bb3eed90 100644
--- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs
@@ -7,6 +7,7 @@ namespace Asp.Versioning.ApiExplorer;
using Asp.Versioning.OData;
using Asp.Versioning.Routing;
using Microsoft.AspNet.OData;
+using Microsoft.AspNet.OData.Builder;
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNet.OData.Formatter;
using Microsoft.AspNet.OData.Routing;
@@ -16,6 +17,7 @@ namespace Asp.Versioning.ApiExplorer;
using Microsoft.OData.UriParser;
using System.Collections.ObjectModel;
using System.Net.Http.Formatting;
+using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Web.Http;
using System.Web.Http.Controllers;
@@ -50,7 +52,11 @@ public ODataApiExplorer( HttpConfiguration configuration )
/// The current HTTP configuration .
/// The associated API explorer options .
public ODataApiExplorer( HttpConfiguration configuration, ODataApiExplorerOptions options )
- : base( configuration, options ) => this.options = options;
+ : base( configuration, options )
+ {
+ this.options = options;
+ options.AdHocModelBuilder.OnModelCreated += MarkAsAdHoc;
+ }
///
/// Gets the options associated with the API explorer.
@@ -72,10 +78,7 @@ protected override bool ShouldExploreAction(
IHttpRoute route,
ApiVersion apiVersion )
{
- if ( actionDescriptor == null )
- {
- throw new ArgumentNullException( nameof( actionDescriptor ) );
- }
+ ArgumentNullException.ThrowIfNull( actionDescriptor );
if ( route is not ODataRoute )
{
@@ -120,15 +123,8 @@ protected override bool ShouldExploreController(
IHttpRoute route,
ApiVersion apiVersion )
{
- if ( controllerDescriptor == null )
- {
- throw new ArgumentNullException( nameof( controllerDescriptor ) );
- }
-
- if ( route == null )
- {
- throw new ArgumentNullException( nameof( route ) );
- }
+ ArgumentNullException.ThrowIfNull( controllerDescriptor );
+ ArgumentNullException.ThrowIfNull( route );
if ( controllerDescriptor.ControllerType.IsMetadataController() )
{
@@ -162,20 +158,30 @@ protected override Collection ExploreRouteControllers(
IHttpRoute route,
ApiVersion apiVersion )
{
- if ( controllerMappings == null )
- {
- throw new ArgumentNullException( nameof( controllerMappings ) );
- }
+ ArgumentNullException.ThrowIfNull( controllerMappings );
Collection apiDescriptions;
if ( route is not ODataRoute )
{
apiDescriptions = base.ExploreRouteControllers( controllerMappings, route, apiVersion );
- return ExploreQueryOptions( route, apiDescriptions );
+
+ if ( Options.AdHocModelBuilder.ModelConfigurations.Count == 0 )
+ {
+ ExploreQueryOptions( route, apiDescriptions );
+ }
+ else if ( apiDescriptions.Count > 0 )
+ {
+ using ( new AdHocEdmScope( apiDescriptions, Options.AdHocModelBuilder ) )
+ {
+ ExploreQueryOptions( route, apiDescriptions );
+ }
+ }
+
+ return apiDescriptions;
}
- apiDescriptions = new();
+ apiDescriptions = [];
var modelSelector = Configuration.GetODataRootContainer( route ).GetRequiredService();
var edmModel = modelSelector.SelectModel( apiVersion );
@@ -199,7 +205,8 @@ protected override Collection ExploreRouteControllers(
}
}
- return ExploreQueryOptions( route, apiDescriptions );
+ ExploreQueryOptions( route, apiDescriptions );
+ return apiDescriptions;
}
///
@@ -210,7 +217,25 @@ protected override Collection ExploreDirectRouteControl
ApiVersion apiVersion )
{
var apiDescriptions = base.ExploreDirectRouteControllers( controllerDescriptor, candidateActionDescriptors, route, apiVersion );
- return ExploreQueryOptions( route, apiDescriptions );
+
+ if ( apiDescriptions.Count == 0 )
+ {
+ return apiDescriptions;
+ }
+
+ if ( Options.AdHocModelBuilder.ModelConfigurations.Count == 0 )
+ {
+ ExploreQueryOptions( route, apiDescriptions );
+ }
+ else if ( apiDescriptions.Count > 0 )
+ {
+ using ( new AdHocEdmScope( apiDescriptions, Options.AdHocModelBuilder ) )
+ {
+ ExploreQueryOptions( route, apiDescriptions );
+ }
+ }
+
+ return apiDescriptions;
}
///
@@ -222,10 +247,7 @@ protected virtual void ExploreQueryOptions(
IEnumerable apiDescriptions,
ODataUriResolver uriResolver )
{
- if ( uriResolver == null )
- {
- throw new ArgumentNullException( nameof( uriResolver ) );
- }
+ ArgumentNullException.ThrowIfNull( uriResolver );
var queryOptions = Options.QueryOptions;
var settings = new ODataQueryOptionSettings()
@@ -238,20 +260,20 @@ protected virtual void ExploreQueryOptions(
queryOptions.ApplyTo( apiDescriptions, settings );
}
- private Collection ExploreQueryOptions(
- IHttpRoute route,
- Collection apiDescriptions )
+ [MethodImpl( MethodImplOptions.AggressiveInlining )]
+ private static void MarkAsAdHoc( ODataModelBuilder builder, IEdmModel model ) =>
+ model.SetAnnotationValue( model, AdHocAnnotation.Instance );
+
+ private void ExploreQueryOptions( IHttpRoute route, Collection apiDescriptions )
{
if ( apiDescriptions.Count == 0 )
{
- return apiDescriptions;
+ return;
}
var uriResolver = Configuration.GetODataRootContainer( route ).GetRequiredService();
ExploreQueryOptions( apiDescriptions, uriResolver );
-
- return apiDescriptions;
}
private ResponseDescription CreateResponseDescriptionWithRoute(
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs
index b9d34d6b..4bf5b113 100644
--- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs
@@ -15,7 +15,8 @@ public partial class ODataApiExplorerOptions : ApiExplorerOptions
/// Initializes a new instance of the class.
///
/// The current configuration associated with the options.
- public ODataApiExplorerOptions( HttpConfiguration configuration ) : base( configuration ) { }
+ public ODataApiExplorerOptions( HttpConfiguration configuration )
+ : base( configuration ) => AdHocModelBuilder = new( configuration );
///
/// Gets or sets a value indicating whether the API explorer settings are honored.
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj
index 44cd9e06..b8bedf18 100644
--- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj
@@ -1,8 +1,8 @@
- 6.2.1
- 6.2.0.0
+ 7.1.0
+ 7.1.0.0
net45;net472
Asp.Versioning
ASP.NET Web API Versioning API Explorer for OData v4.0
@@ -15,8 +15,12 @@
+
+
+
+
@@ -27,6 +31,13 @@
+
+
+
+
+
+
+
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs
new file mode 100644
index 00000000..771af4de
--- /dev/null
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs
@@ -0,0 +1,31 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.Conventions;
+
+using Asp.Versioning.OData;
+using System.Web.Http.Description;
+
+///
+/// Provides additional implementation specific to ASP.NET Web API.
+///
+public partial class ImplicitModelBoundSettingsConvention : IModelConfiguration, IODataQueryOptionsConvention
+{
+ ///
+ public void ApplyTo( ApiDescription apiDescription )
+ {
+ var response = apiDescription.ResponseDescription;
+ var type = response.ResponseType ?? response.DeclaredType;
+
+ if ( type == null )
+ {
+ return;
+ }
+
+ if ( type.IsEnumerable( out var itemType ) )
+ {
+ type = itemType;
+ }
+
+ types.Add( type! );
+ }
+}
\ No newline at end of file
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs
index 1c1a2f49..410f48ff 100644
--- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs
@@ -13,6 +13,6 @@ private void VisitAction( HttpActionDescriptor action )
var attributes = new List( controller.GetCustomAttributes( inherit: true ) );
attributes.AddRange( action.GetCustomAttributes( inherit: true ) );
- VisitEnableQuery( attributes );
+ VisitEnableQuery( attributes.ToArray() );
}
}
\ No newline at end of file
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs
index 04f8cf6d..8b297ebb 100644
--- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs
@@ -2,7 +2,6 @@
namespace Asp.Versioning.Conventions;
-using Microsoft.AspNet.OData;
using System.Runtime.CompilerServices;
using System.Web.Http.Description;
@@ -14,20 +13,4 @@ public partial class ODataQueryOptionsConventionBuilder
[MethodImpl( MethodImplOptions.AggressiveInlining )]
private static Type GetController( ApiDescription apiDescription ) =>
apiDescription.ActionDescriptor.ControllerDescriptor.ControllerType;
-
- [MethodImpl( MethodImplOptions.AggressiveInlining )]
- private static bool IsODataLike( ApiDescription description )
- {
- var parameters = description.ParameterDescriptions;
-
- for ( var i = 0; i < parameters.Count; i++ )
- {
- if ( parameters[i].ParameterDescriptor.ParameterType.IsODataQueryOptions() )
- {
- return true;
- }
- }
-
- return false;
- }
}
\ No newline at end of file
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs
index 7b008e3a..087a1b21 100644
--- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs
@@ -19,10 +19,7 @@ public partial class ODataValidationSettingsConvention
///
public virtual void ApplyTo( ApiDescription apiDescription )
{
- if ( apiDescription == null )
- {
- throw new ArgumentNullException( nameof( apiDescription ) );
- }
+ ArgumentNullException.ThrowIfNull( apiDescription );
if ( !IsSupported( apiDescription.HttpMethod.Method ) )
{
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt
index 40001e18..5f282702 100644
--- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt
@@ -1 +1 @@
-Fix MediaTypeApiVersionReaderBuilder.AddParameters (#904)
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Routing/ODataRouteBuilderContext.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Routing/ODataRouteBuilderContext.cs
index 23fe0a48..3ff17b36 100644
--- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Routing/ODataRouteBuilderContext.cs
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Routing/ODataRouteBuilderContext.cs
@@ -355,7 +355,7 @@ private static bool IsNavigationPropertyLink( IEdmEntitySet? entitySet, IEdmSing
{
if ( propertyNames is null )
{
- propertyNames = new();
+ propertyNames = [];
}
else
{
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/SR.Designer.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/SR.Designer.cs
new file mode 100644
index 00000000..978b3fa3
--- /dev/null
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/SR.Designer.cs
@@ -0,0 +1,72 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Asp.Versioning {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class SR {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal SR() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Asp.Versioning.SR", typeof(SR).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The value cannot be an empty string..
+ ///
+ internal static string Argument_EmptyString {
+ get {
+ return ResourceManager.GetString("Argument_EmptyString", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/SR.resx b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/SR.resx
new file mode 100644
index 00000000..9222b107
--- /dev/null
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/SR.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ The value cannot be an empty string.
+
+
\ No newline at end of file
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/System.Web.Http/Description/ApiDescriptionExtensions.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/System.Web.Http/Description/ApiDescriptionExtensions.cs
index 3c19916d..cba8d13e 100644
--- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/System.Web.Http/Description/ApiDescriptionExtensions.cs
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/System.Web.Http/Description/ApiDescriptionExtensions.cs
@@ -1,100 +1,114 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-namespace System.Web.Http.Description
-{
- using Asp.Versioning.Description;
- using Microsoft.AspNet.OData.Routing;
- using Microsoft.OData.Edm;
+namespace System.Web.Http.Description;
+
+using Asp.Versioning.Description;
+using Microsoft.AspNet.OData.Routing;
+using Microsoft.OData.Edm;
+///
+/// Provides extension methods for the class.
+///
+public static class ApiDescriptionExtensions
+{
///
- /// Provides extension methods for the class.
+ /// Gets the entity data model (EDM) associated with the API description.
///
- public static class ApiDescriptionExtensions
+ /// The API description to get the model for.
+ /// The associated EDM model or null if there is no associated model.
+ public static IEdmModel? EdmModel( this ApiDescription apiDescription )
{
- ///
- /// Gets the entity data model (EDM) associated with the API description.
- ///
- /// The API description to get the model for.
- /// The associated EDM model or null if there is no associated model.
- public static IEdmModel? EdmModel( this ApiDescription apiDescription )
+ if ( apiDescription is VersionedApiDescription description )
{
- if ( apiDescription is VersionedApiDescription description )
- {
- return description.GetProperty();
- }
+ return description.GetProperty();
+ }
+ return default;
+ }
+
+ ///
+ /// Gets the entity set associated with the API description.
+ ///
+ /// The API description to get the entity set for.
+ /// The associated entity set or null if there is no associated entity set.
+ public static IEdmEntitySet? EntitySet( this ApiDescription apiDescription )
+ {
+ if ( apiDescription is not VersionedApiDescription description )
+ {
return default;
}
- ///
- /// Gets the entity set associated with the API description.
- ///
- /// The API description to get the entity set for.
- /// The associated entity set or null if there is no associated entity set.
- public static IEdmEntitySet? EntitySet( this ApiDescription apiDescription )
+ var key = typeof( IEdmEntitySet );
+
+ if ( description.Properties.TryGetValue( key, out var value ) )
{
- if ( apiDescription is not VersionedApiDescription description )
- {
- return default;
- }
+ return (IEdmEntitySet) value;
+ }
- var key = typeof( IEdmEntitySet );
+ var container = description.EdmModel()?.EntityContainer;
- if ( description.Properties.TryGetValue( key, out var value ) )
- {
- return (IEdmEntitySet) value;
- }
+ if ( container == null )
+ {
+ return default;
+ }
- var container = description.EdmModel()?.EntityContainer;
+ var entitySetName = description.ActionDescriptor.ControllerDescriptor.ControllerName;
+ var entitySet = container.FindEntitySet( entitySetName );
- if ( container == null )
- {
- return default;
- }
+ description.Properties[key] = entitySet;
- var entitySetName = description.ActionDescriptor.ControllerDescriptor.ControllerName;
- var entitySet = container.FindEntitySet( entitySetName );
+ return entitySet;
+ }
- description.Properties[key] = entitySet;
+ ///
+ /// Gets the entity type associated with the API description.
+ ///
+ /// The API description to get the entity type for.
+ /// The associated entity type or null if there is no associated entity type.
+ public static IEdmEntityType? EntityType( this ApiDescription apiDescription ) => apiDescription.EntitySet()?.EntityType();
- return entitySet;
+ ///
+ /// Gets the operation associated with the API description.
+ ///
+ /// The API description to get the operation for.
+ /// The associated EDM operation or null if there is no associated operation.
+ public static IEdmOperation? Operation( this ApiDescription apiDescription )
+ {
+ if ( apiDescription is VersionedApiDescription description )
+ {
+ return description.GetProperty();
}
- ///
- /// Gets the entity type associated with the API description.
- ///
- /// The API description to get the entity type for.
- /// The associated entity type or null if there is no associated entity type.
- public static IEdmEntityType? EntityType( this ApiDescription apiDescription ) => apiDescription.EntitySet()?.EntityType();
-
- ///
- /// Gets the operation associated with the API description.
- ///
- /// The API description to get the operation for.
- /// The associated EDM operation or null if there is no associated operation.
- public static IEdmOperation? Operation( this ApiDescription apiDescription )
- {
- if ( apiDescription is VersionedApiDescription description )
- {
- return description.GetProperty();
- }
+ return default;
+ }
- return default;
+ ///
+ /// Gets the route prefix associated with the API description.
+ ///
+ /// The API description to get the route prefix for.
+ /// The associated route prefix or null .
+ public static string? RoutePrefix( this ApiDescription apiDescription )
+ {
+ if ( apiDescription == null )
+ {
+ throw new ArgumentNullException( nameof( apiDescription ) );
}
- ///
- /// Gets the route prefix associated with the API description.
- ///
- /// The API description to get the route prefix for.
- /// The associated route prefix or null .
- public static string? RoutePrefix( this ApiDescription apiDescription )
+ return apiDescription.Route is ODataRoute route ? route.RoutePrefix : default;
+ }
+
+ internal static bool IsODataLike( this ApiDescription description )
+ {
+ var parameters = description.ParameterDescriptions;
+
+ for ( var i = 0; i < parameters.Count; i++ )
{
- if ( apiDescription == null )
+ if ( parameters[i].ParameterDescriptor.ParameterType.IsODataQueryOptions() )
{
- throw new ArgumentNullException( nameof( apiDescription ) );
+ return true;
}
-
- return apiDescription.Route is ODataRoute route ? route.RoutePrefix : default;
}
+
+ return false;
}
}
\ No newline at end of file
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj
index 504275fe..7229ae69 100644
--- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj
@@ -1,8 +1,8 @@
- 6.2.1
- 6.2.0.0
+ 7.1.0
+ 7.1.0.0
net45;net472
Asp.Versioning
API Versioning for ASP.NET Web API with OData v4.0
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/EdmModelSelector.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/EdmModelSelector.cs
index cba6623f..ed7497b9 100644
--- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/EdmModelSelector.cs
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/EdmModelSelector.cs
@@ -53,8 +53,8 @@ public EdmModelSelector( IEnumerable models, IApiVersionSelector apiV
break;
default:
- versions = new();
- collection = new();
+ versions = [];
+ collection = [];
foreach ( var model in models )
{
@@ -130,16 +130,12 @@ public EdmModelSelector( IEnumerable models, IApiVersionSelector apiV
private static void AddVersionFromModel( IEdmModel model, IList versions, IDictionary collection )
{
- var annotation = model.GetAnnotationValue( model );
-
- if ( annotation == null )
+ if ( model.GetApiVersion() is not ApiVersion version )
{
var message = string.Format( CultureInfo.CurrentCulture, SR.MissingAnnotation, typeof( ApiVersionAnnotation ).Name );
throw new ArgumentException( message );
}
- var version = annotation.ApiVersion;
-
collection.Add( version, model );
versions.Add( version );
}
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/VersionedODataModelBuilder.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/VersionedODataModelBuilder.cs
index 7f493939..f08a3c18 100644
--- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/VersionedODataModelBuilder.cs
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/VersionedODataModelBuilder.cs
@@ -78,7 +78,7 @@ protected virtual IReadOnlyList GetApiVersions()
if ( versions.Count > 0 && supported == null )
{
- supported = new();
+ supported = [];
}
for ( var j = 0; j < versions.Count; j++ )
@@ -90,7 +90,7 @@ protected virtual IReadOnlyList GetApiVersions()
if ( versions.Count > 0 && deprecated == null )
{
- deprecated = new();
+ deprecated = [];
}
for ( var j = 0; j < versions.Count; j++ )
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt
index 40001e18..5f282702 100644
--- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt
@@ -1 +1 @@
-Fix MediaTypeApiVersionReaderBuilder.AddParameters (#904)
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs
index 1a255dad..72c1025f 100644
--- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs
+++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs
@@ -1,5 +1,10 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+//// Ignore Spelling: Dlike
+//// Ignore Spelling: Multipart
+//// Ignore Spelling: nonaction
+//// Ignore Spelling: nonquery
+
namespace Asp.Versioning.Conventions;
using Asp.Versioning.Description;
@@ -10,7 +15,6 @@ namespace Asp.Versioning.Conventions;
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNet.OData.Query;
using Microsoft.OData.Edm;
-using System.Collections.ObjectModel;
using System.Net.Http;
using System.Reflection;
using System.Web.Http;
@@ -565,8 +569,8 @@ public static IEnumerable EnableQueryAttributeData
var controller = new Mock() { CallBase = true };
var action = new Mock() { CallBase = true };
- controller.Setup( m => m.GetCustomAttributes( It.IsAny() ) ).Returns( new Collection() );
- action.Setup( m => m.GetCustomAttributes( It.IsAny() ) ).Returns( new Collection() );
+ controller.Setup( m => m.GetCustomAttributes( It.IsAny() ) ).Returns( [] );
+ action.Setup( m => m.GetCustomAttributes( It.IsAny() ) ).Returns( [] );
var actionDescriptor = action.Object;
var responseType = singleResult ? typeof( object ) : typeof( IEnumerable );
@@ -604,7 +608,6 @@ private static ApiDescription NewApiDescription( Type controllerType, Type respo
}
#pragma warning disable IDE0060 // Remove unused parameter
-#pragma warning disable CA1034 // Nested types should not be visible
public class SinglePartController : ODataController
{
diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Description/ODataApiExplorerTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Description/ODataApiExplorerTest.cs
index 534d10a3..34540a00 100644
--- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Description/ODataApiExplorerTest.cs
+++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Description/ODataApiExplorerTest.cs
@@ -3,9 +3,13 @@
namespace Asp.Versioning.Description;
using Asp.Versioning.ApiExplorer;
+using Asp.Versioning.Controllers;
+using Asp.Versioning.Conventions;
using Microsoft.AspNet.OData;
+using Microsoft.AspNet.OData.Extensions;
using System.Net.Http;
using System.Web.Http;
+using System.Web.Http.Dispatcher;
using static System.Net.Http.HttpMethod;
public class ODataApiExplorerTest
@@ -210,4 +214,34 @@ public void api_description_group_should_explore_navigation_properties()
},
options => options.ExcludingMissingMembers() );
}
+
+ [Fact]
+ public void api_description_group_should_explore_model_bound_settings()
+ {
+ // arrange
+ var configuration = new HttpConfiguration();
+ var controllerTypeResolver = new ControllerTypeCollection(
+ typeof( VersionedMetadataController ),
+ typeof( Simulators.V1.BooksController ) );
+
+ configuration.Services.Replace( typeof( IHttpControllerTypeResolver ), controllerTypeResolver );
+ configuration.EnableDependencyInjection();
+ configuration.AddApiVersioning();
+ configuration.MapHttpAttributeRoutes();
+
+ var apiVersion = new ApiVersion( 1.0 );
+ var options = new ODataApiExplorerOptions( configuration );
+ var apiExplorer = new ODataApiExplorer( configuration, options );
+
+ options.AdHocModelBuilder.ModelConfigurations.Add( new ImplicitModelBoundSettingsConvention() );
+
+ // act
+ var descriptionGroup = apiExplorer.ApiDescriptions[apiVersion];
+ var description = descriptionGroup.ApiDescriptions[0];
+
+ // assert
+ var parameter = description.ParameterDescriptions.Single( p => p.Name == "$filter" );
+
+ parameter.Documentation.Should().EndWith( "author, published." );
+ }
}
\ No newline at end of file
diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs
index 7d892736..2e0352bb 100644
--- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs
+++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-
namespace Asp.Versioning.Simulators.Configuration;
using Asp.Versioning.OData;
diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs
index 6dd8b03a..9b04a783 100644
--- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs
+++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-
namespace Asp.Versioning.Simulators.Configuration;
using Asp.Versioning.OData;
diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Book.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Book.cs
index 404bf071..72b12f9b 100644
--- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Book.cs
+++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Book.cs
@@ -2,6 +2,9 @@
namespace Asp.Versioning.Simulators.Models;
+using Microsoft.AspNet.OData.Query;
+
+[Filter( "author", "published" )]
public class Book
{
public string Id { get; set; }
diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs
index 83a08d66..19470142 100644
--- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs
+++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA2227 // Collection properties should be read only
-
namespace Asp.Versioning.Simulators.Models;
public class Supplier
diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs
index e6e24820..bab6814e 100644
--- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs
+++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs
@@ -14,17 +14,18 @@ namespace Asp.Versioning.Simulators.V1;
/// Represents a RESTful service of books.
///
[ApiVersion( 1.0 )]
+[RoutePrefix( "api/books" )]
public class BooksController : ApiController
{
- private static readonly Book[] books = new Book[]
- {
+ private static readonly Book[] books =
+ [
new() { Id = "9781847490599", Title = "Anna Karenina", Author = "Leo Tolstoy", Published = 1878 },
new() { Id = "9780198800545", Title = "War and Peace", Author = "Leo Tolstoy", Published = 1869 },
new() { Id = "9780684801520", Title = "The Great Gatsby", Author = "F. Scott Fitzgerald", Published = 1925 },
new() { Id = "9780486280615", Title = "The Adventures of Huckleberry Finn", Author = "Mark Twain", Published = 1884 },
new() { Id = "9780140430820", Title = "Moby Dick", Author = "Herman Melville", Published = 1851 },
new() { Id = "9780060934347", Title = "Don Quixote", Author = "Miguel de Cervantes", Published = 1605 },
- };
+ ];
///
/// Gets all books.
@@ -33,6 +34,7 @@ public class BooksController : ApiController
/// All available books.
/// The successfully retrieved books.
[HttpGet]
+ [Route]
[ResponseType( typeof( IEnumerable ) )]
public IHttpActionResult Get( ODataQueryOptions options ) =>
Ok( options.ApplyTo( books.AsQueryable() ) );
@@ -46,6 +48,7 @@ public IHttpActionResult Get( ODataQueryOptions options ) =>
/// The book was successfully retrieved.
/// The book does not exist.
[HttpGet]
+ [Route( "{id}" )]
[ResponseType( typeof( Book ) )]
public IHttpActionResult Get( string id, ODataQueryOptions options )
{
diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V3/SuppliersController.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V3/SuppliersController.cs
index f3264880..a27e95ff 100644
--- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V3/SuppliersController.cs
+++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V3/SuppliersController.cs
@@ -1,9 +1,11 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
#pragma warning disable IDE0060 // Remove unused parameter
+#pragma warning disable IDE0079 // Remove unnecessary suppression
#pragma warning disable SA1625 // Element documentation should not be copied and pasted
namespace Asp.Versioning.Simulators.V3;
+#pragma warning restore IDE0079 // Remove unnecessary suppression
using Asp.Versioning.OData;
using Asp.Versioning.Simulators.Models;
diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/System.Web.Http/Description/ApiDescriptionExtensionsTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/System.Web.Http/Description/ApiDescriptionExtensionsTest.cs
index 88a780dc..5d56a57a 100644
--- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/System.Web.Http/Description/ApiDescriptionExtensionsTest.cs
+++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/System.Web.Http/Description/ApiDescriptionExtensionsTest.cs
@@ -68,7 +68,7 @@ private static VersionedApiDescription CreateApiDescription( IEdmModel model )
{
var configuration = new HttpConfiguration();
var controllerType = typeof( Asp.Versioning.Simulators.V1.OrdersController );
- var actionMethod = controllerType.GetRuntimeMethod( "Get", new[] { typeof( int ) } );
+ var actionMethod = controllerType.GetRuntimeMethod( "Get", [typeof( int )] );
var controllerDescriptor = new HttpControllerDescriptor( configuration, "Orders", controllerType );
var actionDescriptor = new ReflectedHttpActionDescriptor( controllerDescriptor, actionMethod );
var apiDescription = new VersionedApiDescription()
diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Asp.Versioning.WebApi.OData.Tests.csproj b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Asp.Versioning.WebApi.OData.Tests.csproj
index 592ea6dc..30285b43 100644
--- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Asp.Versioning.WebApi.OData.Tests.csproj
+++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Asp.Versioning.WebApi.OData.Tests.csproj
@@ -1,9 +1,9 @@
-
+
-
- net452;net472
- Asp.Versioning
-
+
+ net452;net472
+ Asp.Versioning
+
diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs
index 236fa169..c39e10b4 100644
--- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs
+++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs
@@ -37,6 +37,7 @@ public async Task options_should_return_expected_headers()
configuration.AddApiVersioning(
options =>
{
+ options.ReportApiVersions = true;
options.Policies.Sunset( "VersionedMetadata" )
.Link( "policies" )
.Title( "Versioning Policy" )
@@ -81,8 +82,6 @@ public IEnumerable GetServices( Type serviceType )
}
}
-#pragma warning disable CA1812
-
[ApiVersion( "1.0" )]
[ApiVersion( "2.0" )]
private sealed class Controller1 : ODataController
diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/OData/VersionedODataModelBuilderTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/OData/VersionedODataModelBuilderTest.cs
index 7bd0781e..31845519 100644
--- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/OData/VersionedODataModelBuilderTest.cs
+++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/OData/VersionedODataModelBuilderTest.cs
@@ -34,7 +34,7 @@ public void get_edm_models_should_return_expected_results()
var model = builder.GetEdmModels().Single();
// assert
- model.GetAnnotationValue( model ).ApiVersion.Should().Be( apiVersion );
+ model.GetApiVersion().Should().Be( apiVersion );
modelCreated.Verify( f => f( It.IsAny(), model ), Times.Once() );
}
diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs
index 0756895b..f06fc620 100644
--- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs
+++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+//// Ignore Spelling: Dneutral
+
namespace Asp.Versioning.Routing;
using Microsoft.AspNet.OData;
@@ -44,8 +46,6 @@ public void should_map_controller_should_return_expected_result_for_controller_v
result.Should().BeTrue();
}
-#pragma warning disable CA1812
-
[ApiVersionNeutral]
private sealed class NeutralController : ODataController { }
diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs
index 37fece37..c2e86cbc 100644
--- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs
+++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs
@@ -46,7 +46,7 @@ public void map_versioned_odata_route_should_return_expected_result( string rout
var selector = GetODataRootContainer( configuration, routeName ).GetRequiredService();
var routingConventions = GetRoutingConventions( configuration, route );
- selector.ApiVersions.Should().Equal( new ApiVersion[] { new( 1, 0 ), new( 2, 0 ) } );
+ selector.ApiVersions.Should().Equal( [new( 1, 0 ), new( 2, 0 )] );
routingConventions[0].Should().BeOfType();
routingConventions[1].Should().BeOfType();
routingConventions.OfType().Should().BeEmpty();
@@ -94,8 +94,6 @@ private static IReadOnlyList GetRoutingConventions( Htt
return routingConventions.ToArray();
}
-#pragma warning disable CA1812
-
[ApiVersion( "1.0" )]
private sealed class ControllerV1 : ODataController
{
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/ApiExplorerOptions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/ApiExplorerOptions.cs
index 09df17e5..63aaa0ec 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/ApiExplorerOptions.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/ApiExplorerOptions.cs
@@ -44,4 +44,14 @@ public partial class ApiExplorerOptions
///
/// The name associated with the API version route constraint .
public string RouteConstraintName => options.Value.RouteConstraintName;
+
+ ///
+ /// Gets or sets the API version selector.
+ ///
+ /// An API version selector object.
+ public IApiVersionSelector ApiVersionSelector
+ {
+ get => apiVersionSelector ?? options.Value.ApiVersionSelector;
+ set => apiVersionSelector = value;
+ }
}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs
index 83d99d0e..c7585844 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs
@@ -101,7 +101,7 @@ public IDocumentationProvider DocumentationProvider
/// The configured sunset policy manager .
protected ISunsetPolicyManager SunsetPolicyManager
{
- get => sunsetPolicyManager ??= Configuration.DependencyResolver.GetSunsetPolicyManager();
+ get => sunsetPolicyManager ??= Configuration.GetSunsetPolicyManager();
set => sunsetPolicyManager = value;
}
@@ -227,7 +227,7 @@ protected virtual ApiDescriptionGroupCollection InitializeApiDescriptions()
}
var routes = FlattenRoutes( Configuration.Routes ).ToArray();
- var policyManager = Configuration.DependencyResolver.GetSunsetPolicyManager();
+ var policyManager = Configuration.GetSunsetPolicyManager();
foreach ( var apiVersion in FlattenApiVersions( controllerMappings ) )
{
@@ -713,7 +713,7 @@ protected virtual Collection ExploreRouteControllers(
var apiDescriptions = new Collection();
var routeTemplate = route.RouteTemplate;
- string controllerVariableValue;
+ string? controllerVariableValue;
if ( controllerVariableRegex.IsMatch( routeTemplate ) )
{
@@ -734,12 +734,12 @@ protected virtual Collection ExploreRouteControllers(
}
}
else if ( route.Defaults.TryGetValue( RouteValueKeys.Controller, out controllerVariableValue ) &&
- controllerMappings.TryGetValue( controllerVariableValue, out var controllerDescriptor ) )
+ controllerMappings.TryGetValue( controllerVariableValue!, out var controllerDescriptor ) )
{
// bound controller variable {controller = "controllerName"}
foreach ( var nestedControllerDescriptor in controllerDescriptor.AsEnumerable() )
{
- if ( ShouldExploreController( controllerVariableValue, nestedControllerDescriptor, route, apiVersion ) )
+ if ( ShouldExploreController( controllerVariableValue!, nestedControllerDescriptor, route, apiVersion ) )
{
ExploreRouteActions( route, routeTemplate, nestedControllerDescriptor, apiDescriptions, apiVersion );
}
@@ -782,7 +782,7 @@ private void ExploreRouteActions(
return;
}
- string actionVariableValue;
+ string? actionVariableValue;
if ( actionVariableRegex.IsMatch( localPath ) )
{
@@ -935,17 +935,8 @@ private static bool ShouldEmitPrefixes( ICollection par
parameter.CanConvertPropertiesFromString() ) > 1;
}
- private static Type GetCollectionElementType( Type collectionType )
- {
- var elementType = collectionType.GetElementType();
-
- if ( elementType == null )
- {
- elementType = typeof( ICollection<> ).GetGenericBinderTypeArgs( collectionType ).First();
- }
-
- return elementType;
- }
+ private static Type GetCollectionElementType( Type collectionType ) =>
+ collectionType.GetElementType() ?? typeof( ICollection<> ).GetGenericBinderTypeArgs( collectionType ).First();
private static void AddPlaceholderForProperties(
Dictionary parameterValuesForRoute,
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj
index c04f1fe3..9622bbfd 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj
@@ -1,8 +1,8 @@
- 6.2.1
- 6.2.0.0
+ 7.1.0
+ 7.1.0.0
net45;net472
ASP.NET Web API Versioning API Explorer
The API Explorer extensions for ASP.NET Web API Versioning.
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiDescriptionGroup.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiDescriptionGroup.cs
index 596bd634..469c44f5 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiDescriptionGroup.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiDescriptionGroup.cs
@@ -56,5 +56,5 @@ public ApiDescriptionGroup( ApiVersion apiVersion, string name )
///
/// A collection of
/// versioned API descriptions .
- public virtual Collection ApiDescriptions { get; } = new();
+ public virtual Collection ApiDescriptions { get; } = [];
}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiVersionParameterDescriptionContext.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiVersionParameterDescriptionContext.cs
index c1e5fdf6..f92c6857 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiVersionParameterDescriptionContext.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiVersionParameterDescriptionContext.cs
@@ -30,9 +30,9 @@ public class ApiVersionParameterDescriptionContext : IApiVersionParameterDescrip
public ApiVersionParameterDescriptionContext( ApiDescription apiDescription, ApiVersion apiVersion, ApiExplorerOptions options )
{
Options = options ?? throw new ArgumentNullException( nameof( options ) );
- ApiDescription = apiDescription;
- ApiVersion = apiVersion;
- optional = options.AssumeDefaultVersionWhenUnspecified && apiVersion == options.DefaultApiVersion;
+ ApiDescription = apiDescription ?? throw new ArgumentNullException( nameof( apiDescription ) );
+ ApiVersion = apiVersion ?? throw new ArgumentNullException( nameof( apiVersion ) );
+ optional = FirstParameterIsOptional( apiDescription, apiVersion, options );
}
///
@@ -242,4 +242,21 @@ private static void CloneFormattersAndAddMediaTypeParameter( NameValueHeaderValu
formatters.Add( formatter );
}
}
+
+ private static bool FirstParameterIsOptional(
+ ApiDescription apiDescription,
+ ApiVersion apiVersion,
+ ApiExplorerOptions options )
+ {
+ if ( !options.AssumeDefaultVersionWhenUnspecified )
+ {
+ return false;
+ }
+
+ var mapping = ApiVersionMapping.Explicit | ApiVersionMapping.Implicit;
+ var model = apiDescription.ActionDescriptor.GetApiVersionMetadata().Map( mapping );
+ var defaultApiVersion = options.ApiVersionSelector.SelectVersion( model );
+
+ return apiVersion == defaultApiVersion;
+ }
}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/VersionedApiDescription.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/VersionedApiDescription.cs
index e5ab4a68..da07e7d1 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/VersionedApiDescription.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/VersionedApiDescription.cs
@@ -69,7 +69,7 @@ public ApiVersion ApiVersion
///
/// A collection of arbitrary metadata properties
/// associated with the API description .
- public IDictionary Properties => properties ??= new();
+ public IDictionary Properties => properties ??= [];
private static Action CreateSetResponseDescriptionMutator()
{
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt
index 40001e18..5f282702 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt
@@ -1 +1 @@
-Fix MediaTypeApiVersionReaderBuilder.AddParameters (#904)
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ApiVersionRequestProperties.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ApiVersionRequestProperties.cs
index 98adf3be..0383fb9c 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ApiVersionRequestProperties.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ApiVersionRequestProperties.cs
@@ -55,11 +55,9 @@ public string? RawRequestedApiVersion
{
0 => default,
1 => values[0],
-#pragma warning disable CA1065 // Do not raise exceptions in unexpected locations; existing behavior via IApiVersionReader.Read
_ => throw new AmbiguousApiVersionException(
string.Format( CultureInfo.CurrentCulture, CommonSR.MultipleDifferentApiVersionsRequested, string.Join( ", ", values ) ),
values ),
-#pragma warning restore CA1065
};
}
set
@@ -90,7 +88,7 @@ public ApiVersion? RequestedApiVersion
return apiVersion;
}
- var parser = request.GetConfiguration().DependencyResolver.GetApiVersionParser();
+ var parser = request.GetConfiguration().GetApiVersionParser();
try
{
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj
index b2f49ecd..761b386b 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj
@@ -1,8 +1,8 @@
- 6.2.1
- 6.2.0.0
+ 7.1.0
+ 7.1.0.0
net45;net472
ASP.NET Web API Versioning
A service API versioning library for Microsoft ASP.NET Web API.
@@ -15,8 +15,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs
index 28f69ac4..4ae17299 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs
@@ -23,10 +23,10 @@ namespace Asp.Versioning.Controllers;
///
internal sealed class ActionSelectorCacheItem
{
- private static readonly HttpMethod[] cacheListMethodKinds = new[] { HttpMethod.Get, HttpMethod.Put, HttpMethod.Post };
+ private static readonly HttpMethod[] cacheListMethodKinds = [HttpMethod.Get, HttpMethod.Put, HttpMethod.Post];
private readonly HttpControllerDescriptor controllerDescriptor;
private readonly CandidateAction[] combinedCandidateActions;
- private readonly Dictionary actionParameterNames = new();
+ private readonly Dictionary actionParameterNames = [];
private readonly ILookup combinedActionNameMapping;
private StandardActionSelectionCache? standardActions;
@@ -76,7 +76,7 @@ private void InitializeStandardActions()
if ( controllerDescriptor.IsAttributeRouted() )
{
- selectionCache.StandardCandidateActions = Array.Empty();
+ selectionCache.StandardCandidateActions = [];
}
else
{
@@ -94,7 +94,7 @@ private void InitializeStandardActions()
}
}
- selectionCache.StandardCandidateActions = standardCandidateActions.ToArray();
+ selectionCache.StandardCandidateActions = [.. standardCandidateActions];
}
selectionCache.StandardActionNameMapping =
@@ -118,11 +118,7 @@ private void InitializeStandardActions()
HttpControllerContext controllerContext,
Func, HttpActionDescriptor?> selector )
{
- if ( selector == null )
- {
- throw new ArgumentNullException( nameof( selector ) );
- }
-
+ ArgumentNullException.ThrowIfNull( selector );
InitializeStandardActions();
var firstAttempt = FindAction( controllerContext, selector, ignoreSubRoutes: false );
@@ -153,8 +149,6 @@ private ActionSelectionResult FindAction(
bool ignoreSubRoutes )
{
var selectedCandidates = FindMatchingActions( controllerContext, ignoreSubRoutes );
-
-#pragma warning disable CA2000 // Dispose objects before losing scope
if ( selectedCandidates.Count == 0 )
{
return new( new HttpResponseException( CreateSelectionError( controllerContext ) ) );
@@ -170,7 +164,6 @@ private ActionSelectionResult FindAction(
{
return new( new HttpResponseException( CreateSelectionError( controllerContext ) ) );
}
-#pragma warning restore CA2000 // Dispose objects before losing scope
var ambiguityList = CreateAmbiguousMatchList( selectedCandidates );
var message = string.Format( CultureInfo.CurrentCulture, SR.ApiControllerActionSelector_AmbiguousMatch, ambiguityList );
@@ -307,7 +300,7 @@ private static List GetInitialCandidateWithParameterL
continue;
}
- subRouteData.Values.TryGetValue( RouteValueKeys.Action, out string actionName );
+ subRouteData.Values.TryGetValue( RouteValueKeys.Action, out string? actionName );
for ( var i = 0; i < candidates.Length; i++ )
{
@@ -338,9 +331,9 @@ private CandidateAction[] GetInitialCandidateList( HttpControllerContext control
var routeData = controllerContext.RouteData;
CandidateAction[] candidates;
- if ( routeData.Values.TryGetValue( RouteValueKeys.Action, out string actionName ) )
+ if ( routeData.Values.TryGetValue( RouteValueKeys.Action, out string? actionName ) )
{
- var actionsFoundByName = standardActions!.StandardActionNameMapping![actionName].ToArray();
+ var actionsFoundByName = standardActions!.StandardActionNameMapping![actionName!].ToArray();
if ( actionsFoundByName.Length == 0 )
{
@@ -415,7 +408,7 @@ private List FindActionMatchRequiredRouteAndQueryPara
}
private List FindActionMatchMostRouteAndQueryParameters( List candidatesFound ) =>
- candidatesFound.Count < 2 ? candidatesFound : candidatesFound.GroupBy( c => actionParameterNames[c.ActionDescriptor].Length ).OrderByDescending( g => g.Key ).First().ToList();
+ candidatesFound.Count < 2 ? candidatesFound : [.. candidatesFound.GroupBy( c => actionParameterNames[c.ActionDescriptor].Length ).OrderByDescending( g => g.Key ).First()];
private static CandidateActionWithParams[] GetCandidateActionsWithBindings( HttpControllerContext controllerContext, CandidateAction[] candidatesFound )
{
@@ -488,7 +481,7 @@ private static CandidateAction[] FindActionsForMethod( HttpMethod method, Candid
{
var listCandidates = new List();
FindActionsForMethod( method, candidates, listCandidates );
- return listCandidates.ToArray();
+ return [.. listCandidates];
}
private static void FindActionsForMethod( HttpMethod method, CandidateAction[] candidates, List listCandidates )
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionActionSelector.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionActionSelector.cs
index a8b5a729..e23f72a8 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionActionSelector.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionActionSelector.cs
@@ -24,11 +24,7 @@ public class ApiVersionActionSelector : IHttpActionSelector
/// controller context .
public virtual HttpActionDescriptor? SelectAction( HttpControllerContext controllerContext )
{
- if ( controllerContext == null )
- {
- throw new ArgumentNullException( nameof( controllerContext ) );
- }
-
+ ArgumentNullException.ThrowIfNull( controllerContext );
var internalSelector = GetInternalSelector( controllerContext.ControllerDescriptor );
return internalSelector.SelectAction( controllerContext, SelectActionVersion );
}
@@ -41,10 +37,7 @@ public class ApiVersionActionSelector : IHttpActionSelector
/// specified controller descriptor .
public virtual ILookup GetActionMapping( HttpControllerDescriptor controllerDescriptor )
{
- if ( controllerDescriptor == null )
- {
- throw new ArgumentNullException( nameof( controllerDescriptor ) );
- }
+ ArgumentNullException.ThrowIfNull( controllerDescriptor );
var actionMappings = ( from descriptor in controllerDescriptor.AsEnumerable( includeCandidates: true )
let selector = GetInternalSelector( descriptor )
@@ -65,15 +58,8 @@ public virtual ILookup GetActionMapping( HttpContr
/// ambiguous among the provided list of candidate actions .
protected virtual HttpActionDescriptor? SelectActionVersion( HttpControllerContext controllerContext, IReadOnlyList candidateActions )
{
- if ( controllerContext == null )
- {
- throw new ArgumentNullException( nameof( controllerContext ) );
- }
-
- if ( candidateActions == null )
- {
- throw new ArgumentNullException( nameof( candidateActions ) );
- }
+ ArgumentNullException.ThrowIfNull( controllerContext );
+ ArgumentNullException.ThrowIfNull( candidateActions );
if ( candidateActions.Count == 0 )
{
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionParameterBinding.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionParameterBinding.cs
index cc34b6a8..a9f4d9af 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionParameterBinding.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionParameterBinding.cs
@@ -25,11 +25,7 @@ public override Task ExecuteBindingAsync(
HttpActionContext actionContext,
CancellationToken cancellationToken )
{
- if ( actionContext == null )
- {
- throw new ArgumentNullException( nameof( actionContext ) );
- }
-
+ ArgumentNullException.ThrowIfNull( actionContext );
var value = actionContext.Request.ApiVersionProperties().RequestedApiVersion;
SetValue( actionContext, value );
return CompletedTask;
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/HttpControllerDescriptorGroup.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/HttpControllerDescriptorGroup.cs
index 4e3aab24..884a1747 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/HttpControllerDescriptorGroup.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/HttpControllerDescriptorGroup.cs
@@ -32,7 +32,7 @@ public class HttpControllerDescriptorGroup : HttpControllerDescriptor, IReadOnly
/// HTTP controller descriptors .
public HttpControllerDescriptorGroup( HttpConfiguration configuration, string controllerName, params HttpControllerDescriptor[] controllerDescriptors )
: base( configuration, controllerName, controllerDescriptors?[0].ControllerType ) =>
- descriptors = controllerDescriptors ?? throw new ArgumentNullException( nameof( controllerDescriptors ) );
+ descriptors = controllerDescriptors ?? throw new System.ArgumentNullException( nameof( controllerDescriptors ) );
///
/// Initializes a new instance of the class.
@@ -50,7 +50,7 @@ public HttpControllerDescriptorGroup( HttpConfiguration configuration, string co
/// HTTP controller descriptors .
public HttpControllerDescriptorGroup( HttpConfiguration configuration, string controllerName, IReadOnlyList controllerDescriptors )
: base( configuration, controllerName, controllerDescriptors?[0].ControllerType ) =>
- descriptors = controllerDescriptors ?? throw new ArgumentNullException( nameof( controllerDescriptors ) );
+ descriptors = controllerDescriptors ?? throw new System.ArgumentNullException( nameof( controllerDescriptors ) );
///
/// Creates and returns a controller for the specified request.
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ActionApiVersionConventionBuilderBase.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ActionApiVersionConventionBuilderBase.cs
index aea7e59b..9dd43313 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ActionApiVersionConventionBuilderBase.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ActionApiVersionConventionBuilderBase.cs
@@ -13,10 +13,7 @@ public partial class ActionApiVersionConventionBuilderBase : IApiVersionConventi
///
public virtual void ApplyTo( HttpActionDescriptor item )
{
- if ( item == null )
- {
- throw new ArgumentNullException( nameof( item ) );
- }
+ ArgumentNullException.ThrowIfNull( item );
var attributes = new List();
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ControllerApiVersionConventionBuilderBase.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ControllerApiVersionConventionBuilderBase.cs
index 0c194afe..1480d3ba 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ControllerApiVersionConventionBuilderBase.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ControllerApiVersionConventionBuilderBase.cs
@@ -35,10 +35,7 @@ public abstract class ControllerApiVersionConventionBuilderBase : ApiVersionConv
/// to apply the conventions to.
public virtual void ApplyTo( HttpControllerDescriptor item )
{
- if ( item == null )
- {
- throw new ArgumentNullException( nameof( item ) );
- }
+ ArgumentNullException.ThrowIfNull( item );
var attributes = new List();
@@ -54,7 +51,7 @@ public virtual void ApplyTo( HttpControllerDescriptor item )
/// The method representing the action to retrieve the convention for.
/// The retrieved convention or null .
/// True if the convention was successfully retrieved; otherwise, false.
- protected abstract bool TryGetConvention( MethodInfo method, out IApiVersionConvention convention );
+ protected abstract bool TryGetConvention( MethodInfo method, [MaybeNullWhen( false )] out IApiVersionConvention convention );
private void ApplyActionConventions( HttpControllerDescriptor controller )
{
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DefaultApiVersionReporter.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DefaultApiVersionReporter.cs
index 357b5978..caa7f0be 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DefaultApiVersionReporter.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DefaultApiVersionReporter.cs
@@ -10,10 +10,6 @@ namespace Asp.Versioning;
///
public partial class DefaultApiVersionReporter
{
- private static DefaultApiVersionReporter? instance;
-
- internal static IReportApiVersions Instance => instance ??= new();
-
private static void AddApiVersionHeader( HttpResponseHeaders headers, string headerName, IReadOnlyList versions )
{
if ( versions.Count == 0 || headers.Contains( headerName ) )
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dependencies/DefaultContainer.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dependencies/DefaultContainer.cs
new file mode 100644
index 00000000..7725393c
--- /dev/null
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dependencies/DefaultContainer.cs
@@ -0,0 +1,84 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.Dependencies;
+
+using Asp.Versioning;
+using Asp.Versioning.Conventions;
+using System.ComponentModel.Design;
+using System.Web.Http.Dependencies;
+
+internal sealed class DefaultContainer : IDependencyResolver, IDependencyScope
+{
+ private readonly ServiceContainer container = new();
+ private bool disposed;
+
+ internal DefaultContainer()
+ {
+ container.AddService( typeof( ApiVersioningOptions ), static ( sc, t ) => new ApiVersioningOptions() );
+ container.AddService( typeof( IApiVersionParser ), static ( sc, t ) => ApiVersionParser.Default );
+ container.AddService( typeof( IControllerNameConvention ), static ( sc, t ) => ControllerNameConvention.Default );
+ container.AddService( typeof( IProblemDetailsFactory ), static ( sc, t ) => new ProblemDetailsFactory() );
+ container.AddService( typeof( ISunsetPolicyManager ), NewSunsetPolicyManager );
+ container.AddService( typeof( IReportApiVersions ), NewApiVersionReporter );
+ }
+
+ public ApiVersioningOptions ApiVersioningOptions
+ {
+ get => GetApiVersioningOptions( container );
+ set
+ {
+ container.RemoveService( typeof( ApiVersioningOptions ) );
+ container.AddService( typeof( ApiVersioningOptions ), value );
+ }
+ }
+
+ public void Replace( Type serviceType, ServiceCreatorCallback activator )
+ {
+ container.RemoveService( serviceType );
+ container.AddService( serviceType, activator );
+ }
+
+ public IDependencyScope BeginScope() => this;
+
+ public void Dispose()
+ {
+ if ( disposed )
+ {
+ return;
+ }
+
+ disposed = true;
+ container.Dispose();
+ }
+
+ public object GetService( Type serviceType ) => container.GetService( serviceType );
+
+ public IEnumerable GetServices( Type serviceType )
+ {
+ var service = container.GetService( serviceType );
+
+ if ( service is not null )
+ {
+ yield return service;
+ }
+ }
+
+ private static ApiVersioningOptions GetApiVersioningOptions( IServiceProvider serviceProvider ) =>
+ (ApiVersioningOptions) serviceProvider.GetService( typeof( ApiVersioningOptions ) );
+
+ private static ISunsetPolicyManager NewSunsetPolicyManager( IServiceProvider serviceProvider, Type type ) =>
+ new SunsetPolicyManager( GetApiVersioningOptions( serviceProvider ) );
+
+ private static IReportApiVersions NewApiVersionReporter( IServiceProvider serviceProvider, Type type )
+ {
+ var options = GetApiVersioningOptions( serviceProvider );
+
+ if ( options.ReportApiVersions )
+ {
+ var sunsetPolicyManager = (ISunsetPolicyManager) serviceProvider.GetService( typeof( ISunsetPolicyManager ) );
+ return new DefaultApiVersionReporter( sunsetPolicyManager );
+ }
+
+ return new DoNotReportApiVersions();
+ }
+}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs
index 9e8c119a..6cad7f56 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs
@@ -3,24 +3,39 @@
namespace Asp.Versioning;
using Asp.Versioning.Conventions;
+using System.Globalization;
+using System.Web.Http;
using System.Web.Http.Dependencies;
internal static class DependencyResolverExtensions
{
- internal static TService? GetService( this IDependencyResolver resolver ) => (TService) resolver.GetService( typeof( TService ) );
-
- internal static IApiVersionParser GetApiVersionParser( this IDependencyResolver resolver ) =>
- resolver.GetService() ?? ApiVersionParser.Default;
-
- internal static IReportApiVersions GetApiVersionReporter( this IDependencyResolver resolver ) =>
- resolver.GetService() ?? DefaultApiVersionReporter.Instance;
-
- internal static IControllerNameConvention GetControllerNameConvention( this IDependencyResolver resolver ) =>
- resolver.GetService() ?? ControllerNameConvention.Default;
-
- internal static IProblemDetailsFactory GetProblemDetailsFactory( this IDependencyResolver resolver ) =>
- resolver.GetService() ?? ProblemDetailsFactory.Default;
-
- internal static ISunsetPolicyManager GetSunsetPolicyManager( this IDependencyResolver resolver ) =>
- resolver.GetService() ?? SunsetPolicyManager.Default;
+ internal static TService? GetService( this IDependencyResolver resolver ) =>
+ (TService) resolver.GetService( typeof( TService ) );
+
+ internal static TService GetRequiredService( this IDependencyResolver resolver )
+ {
+ var service = resolver.GetService();
+ var message = string.Format( CultureInfo.CurrentCulture, SR.NoServiceRegistered, typeof( TService ) );
+ return service ?? throw new InvalidOperationException( message );
+ }
+
+ internal static IApiVersionParser GetApiVersionParser( this HttpConfiguration configuration ) =>
+ configuration.DependencyResolver.GetService() ??
+ configuration.ApiVersioningServices().GetRequiredService();
+
+ internal static IReportApiVersions GetApiVersionReporter( this HttpConfiguration configuration ) =>
+ configuration.DependencyResolver.GetService() ??
+ configuration.ApiVersioningServices().GetRequiredService();
+
+ internal static IControllerNameConvention GetControllerNameConvention( this HttpConfiguration configuration ) =>
+ configuration.DependencyResolver.GetService() ??
+ configuration.ApiVersioningServices().GetRequiredService();
+
+ internal static IProblemDetailsFactory GetProblemDetailsFactory( this HttpConfiguration configuration ) =>
+ configuration.DependencyResolver.GetService() ??
+ configuration.ApiVersioningServices().GetRequiredService();
+
+ internal static ISunsetPolicyManager GetSunsetPolicyManager( this HttpConfiguration configuration ) =>
+ configuration.DependencyResolver.GetService() ??
+ configuration.ApiVersioningServices().GetRequiredService();
}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs
index a9473c8a..c889b994 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs
@@ -123,7 +123,7 @@ public virtual IDictionary GetControllerMappin
return null;
}
- if ( routeData.Values.TryGetValue( RouteDataTokenKeys.Controller, out string controller ) )
+ if ( routeData.Values.TryGetValue( RouteDataTokenKeys.Controller, out string? controller ) )
{
return controller;
}
@@ -221,7 +221,7 @@ private static void ApplyImplicitConventions( HttpControllerDescriptor controlle
}
var actions = mapping.SelectMany( g => g );
- var namingConvention = controller.Configuration.DependencyResolver.GetControllerNameConvention();
+ var namingConvention = controller.Configuration.GetControllerNameConvention();
var name = namingConvention.GroupName( controller.ControllerName );
var metadata = new ApiVersionMetadata( implicitVersionModel, implicitVersionModel, name );
@@ -258,7 +258,7 @@ private static HttpControllerDescriptor[] ApplyCollatedModels(
CollateControllerModels( controllerModels, visitedControllers, CollateActionModels( actionModels, visitedActions ) );
ApplyCollatedModelsToActions( configuration, visitedActions );
- return controllers.ToArray();
+ return [.. controllers];
}
private static void CollateControllerVersions(
@@ -344,7 +344,7 @@ private static void ApplyCollatedModelsToActions(
HttpConfiguration configuration,
List> visitedActions )
{
- var namingConvention = configuration.DependencyResolver.GetControllerNameConvention();
+ var namingConvention = configuration.GetControllerNameConvention();
for ( var i = 0; i < visitedActions.Count; i++ )
{
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ControllerSelectionContext.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ControllerSelectionContext.cs
index 5c8003c1..729390ce 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ControllerSelectionContext.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ControllerSelectionContext.cs
@@ -70,7 +70,7 @@ internal ApiVersion? RequestedVersion
foreach ( var action in actions )
{
- candidates ??= new();
+ candidates ??= [];
candidates.Add( new( action ) );
}
}
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpControllerTypeCache.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpControllerTypeCache.cs
index 473f9811..5c5d3abb 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpControllerTypeCache.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpControllerTypeCache.cs
@@ -33,7 +33,7 @@ private Dictionary> InitializeCache()
var services = configuration.Services;
var assembliesResolver = services.GetAssembliesResolver();
var typeResolver = services.GetHttpControllerTypeResolver();
- var convention = configuration.DependencyResolver.GetControllerNameConvention();
+ var convention = configuration.GetControllerNameConvention();
var comparer = StringComparer.OrdinalIgnoreCase;
return typeResolver.GetControllerTypes( assembliesResolver )
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpResponseExceptionFactory.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpResponseExceptionFactory.cs
index 82a9761d..22fac02a 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpResponseExceptionFactory.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpResponseExceptionFactory.cs
@@ -9,8 +9,6 @@ namespace Asp.Versioning.Dispatcher;
using System.Web.Http.Tracing;
using static System.Net.HttpStatusCode;
-#pragma warning disable CA2000 // Dispose objects before losing scope
-
internal sealed class HttpResponseExceptionFactory
{
private const string Allow = nameof( Allow );
@@ -38,7 +36,7 @@ internal HttpResponseExceptionFactory( HttpRequestMessage request, ApiVersionMod
private ApiVersioningOptions Options => configuration.GetApiVersioningOptions();
- private IProblemDetailsFactory ProblemDetails => configuration.DependencyResolver.GetProblemDetailsFactory();
+ private IProblemDetailsFactory ProblemDetails => configuration.GetProblemDetailsFactory();
private ITraceWriter TraceWriter => configuration.Services.GetTraceWriter() ?? NullTraceWriter.Instance;
@@ -64,7 +62,8 @@ internal HttpResponseException NewUnmatchedException(
}
}
- var versionsOnlyByMediaType = Options.ApiVersionReader.VersionsByMediaType( allowMultipleLocations: false );
+ var options = Options;
+ var versionsOnlyByMediaType = options.ApiVersionReader.VersionsByMediaType( allowMultipleLocations: false );
if ( versionsOnlyByMediaType )
{
@@ -75,9 +74,28 @@ internal HttpResponseException NewUnmatchedException(
if ( couldMatch )
{
properties ??= request.ApiVersionProperties();
- response = properties.RequestedApiVersion is ApiVersion apiVersion
- ? CreateResponseForUnsupportedApiVersion( apiVersion, NotFound )
- : CreateNotFound( conventionRouteResult );
+
+ if ( properties.RequestedApiVersion is ApiVersion apiVersion )
+ {
+ HttpStatusCode statusCode;
+ var matchedUrlSegment = !string.IsNullOrEmpty( properties.RouteParameter );
+
+ if ( matchedUrlSegment )
+ {
+ statusCode = NotFound;
+ }
+ else
+ {
+ var versionsByUrlOnly = options.ApiVersionReader.VersionsByUrl( allowMultipleLocations: false );
+ statusCode = versionsByUrlOnly ? NotFound : options.UnsupportedApiVersionStatusCode;
+ }
+
+ response = CreateResponseForUnsupportedApiVersion( apiVersion, statusCode );
+ }
+ else
+ {
+ response = CreateNotFound( conventionRouteResult );
+ }
}
else
{
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DoNotReportApiVersions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DoNotReportApiVersions.cs
index 722073e5..db2b979d 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DoNotReportApiVersions.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DoNotReportApiVersions.cs
@@ -6,12 +6,6 @@ namespace Asp.Versioning;
internal sealed class DoNotReportApiVersions : IReportApiVersions
{
- private static DoNotReportApiVersions? instance;
-
- private DoNotReportApiVersions() { }
-
- internal static IReportApiVersions Instance => instance ??= new();
-
public ApiVersionMapping Mapping => Explicit | Implicit;
public void Report( HttpResponseMessage response, ApiVersionModel apiVersionModel ) { }
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ErrorObjectFactory.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ErrorObjectFactory.cs
new file mode 100644
index 00000000..79bbe3cc
--- /dev/null
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ErrorObjectFactory.cs
@@ -0,0 +1,94 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+// REF: https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/Infrastructure/DefaultProblemDetailsFactory.cs
+namespace Asp.Versioning;
+
+using Newtonsoft.Json;
+using static Asp.Versioning.ProblemDetailsDefaults;
+using static Newtonsoft.Json.NullValueHandling;
+
+internal sealed class ErrorObjectFactory : IProblemDetailsFactory
+{
+ public ProblemDetails CreateProblemDetails(
+ HttpRequestMessage request,
+ int? statusCode = null,
+ string? title = null,
+ string? type = null,
+ string? detail = null,
+ string? instance = null )
+ {
+ var status = statusCode ?? 500;
+ ErrorObject? problem;
+
+ if ( type == Ambiguous.Type )
+ {
+ problem = NewError( title, instance );
+ problem.Error.Code = Ambiguous.Code;
+ }
+ else if ( type == Invalid.Type )
+ {
+ problem = NewError( title, instance );
+ problem.Error.Code = Invalid.Code;
+ return ProblemDetailsFactory.AddInvalidExtensions( request, status, problem, ApplyMessage );
+ }
+ else if ( type == Unspecified.Type )
+ {
+ problem = NewError( title, instance );
+ problem.Error.Code = Unspecified.Code;
+ }
+ else if ( type == Unsupported.Type )
+ {
+ problem = NewError( title, instance );
+ problem.Error.Code = Unsupported.Code;
+ return ProblemDetailsFactory.AddUnsupportedExtensions( request, status, problem, ApplyMessage );
+ }
+
+ return ProblemDetailsFactory.NewProblemDetails(
+ request,
+ statusCode,
+ title,
+ type,
+ detail,
+ instance );
+ }
+
+ private static ErrorObject NewError( string? message, string? target ) =>
+ new()
+ {
+ Error =
+ {
+ Message = message,
+ Target = target,
+ },
+ };
+
+ private static void ApplyMessage( ErrorObject obj, string message ) =>
+ obj.Error.InnerError = new() { Message = message };
+
+ private sealed class ErrorObject : ProblemDetails
+ {
+ [JsonProperty( "error" )]
+ public ErrorDetail Error { get; } = new();
+ }
+
+ private sealed class ErrorDetail
+ {
+ [JsonProperty( "code", NullValueHandling = Ignore )]
+ public string? Code { get; set; }
+
+ [JsonProperty( "message", NullValueHandling = Ignore )]
+ public string? Message { get; set; }
+
+ [JsonProperty( "target", NullValueHandling = Ignore )]
+ public string? Target { get; set; }
+
+ [JsonProperty( "innerError", NullValueHandling = Ignore )]
+ public InnerError? InnerError { get; set; }
+ }
+
+ private sealed class InnerError
+ {
+ [JsonProperty( "message", NullValueHandling = Ignore )]
+ public string? Message { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Formatting/ProblemDetailsMediaTypeFormatter.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Formatting/ProblemDetailsMediaTypeFormatter.cs
new file mode 100644
index 00000000..cf44d707
--- /dev/null
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Formatting/ProblemDetailsMediaTypeFormatter.cs
@@ -0,0 +1,69 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+// REF: https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/Infrastructure/DefaultProblemDetailsFactory.cs
+namespace Asp.Versioning.Formatting;
+
+using System.IO;
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Formatting;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using static Asp.Versioning.ProblemDetailsDefaults;
+
+///
+/// Represents a media type formatter for problem details based on https://tools.ietf.org/html/rfc7807.
+///
+public class ProblemDetailsMediaTypeFormatter : MediaTypeFormatter
+{
+ private readonly JsonMediaTypeFormatter json;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ProblemDetailsMediaTypeFormatter() : this( new() ) { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The existing instance to derive from.
+ public ProblemDetailsMediaTypeFormatter( JsonMediaTypeFormatter formatter )
+ : base( formatter )
+ {
+ json = formatter;
+ SupportedEncodings.Add( new UTF8Encoding( encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true ) );
+ SupportedEncodings.Add( new UnicodeEncoding( bigEndian: false, byteOrderMark: true, throwOnInvalidBytes: true ) );
+ SupportedMediaTypes.Add( DefaultMediaType );
+ }
+
+ ///
+ /// Gets the default media type.
+ ///
+ /// Returns the media type for application/problem+json.
+ public static MediaTypeHeaderValue DefaultMediaType { get; } = MediaTypeHeaderValue.Parse( MediaType.Json );
+
+ ///
+ public override bool CanReadType( Type type ) => false;
+
+ ///
+ public override bool CanWriteType( Type type ) => typeof( ProblemDetails ).IsAssignableFrom( type );
+
+ ///
+ public override Task WriteToStreamAsync(
+ Type type,
+ object value,
+ Stream writeStream,
+ HttpContent content,
+ TransportContext transportContext,
+ CancellationToken cancellationToken ) =>
+ json.WriteToStreamAsync( type, value, writeStream, content, transportContext, cancellationToken );
+
+ ///
+ public override void SetDefaultContentHeaders( Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType )
+ {
+ mediaType.MediaType = DefaultMediaType.MediaType;
+ base.SetDefaultContentHeaders( type, headers, mediaType );
+ }
+}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/HeaderApiVersionReader.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/HeaderApiVersionReader.cs
index b18405aa..58b12d19 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/HeaderApiVersionReader.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/HeaderApiVersionReader.cs
@@ -10,10 +10,7 @@ public partial class HeaderApiVersionReader
///
public virtual IReadOnlyList Read( HttpRequestMessage request )
{
- if ( request == null )
- {
- throw new ArgumentNullException( nameof( request ) );
- }
+ ArgumentNullException.ThrowIfNull( request );
var count = HeaderNames.Count;
@@ -68,7 +65,7 @@ public virtual IReadOnlyList Read( HttpRequestMessage request )
if ( versions == null )
{
- return version == null ? Array.Empty() : new[] { version };
+ return version == null ? [] : [version];
}
return versions.ToArray();
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/IApiVersionSelectorExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/IApiVersionSelectorExtensions.cs
new file mode 100644
index 00000000..37d63336
--- /dev/null
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/IApiVersionSelectorExtensions.cs
@@ -0,0 +1,22 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+///
+/// Provides extension methods for .
+///
+public static class IApiVersionSelectorExtensions
+{
+ ///
+ /// Selects an API version given the specified API version information.
+ ///
+ /// The extended .
+ /// The model to select the version from.
+ /// The selected API version .
+ public static ApiVersion SelectVersion( this IApiVersionSelector selector, ApiVersionModel model )
+ {
+ ArgumentNullException.ThrowIfNull( selector );
+ using var request = new HttpRequestMessage();
+ return selector.SelectVersion( request, model );
+ }
+}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReader.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReader.cs
index d748aa0b..d3a96174 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReader.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReader.cs
@@ -10,10 +10,7 @@ public partial class MediaTypeApiVersionReader
///
public virtual IReadOnlyList Read( HttpRequestMessage request )
{
- if ( request == null )
- {
- throw new ArgumentNullException( nameof( request ) );
- }
+ ArgumentNullException.ThrowIfNull( request );
var contentType = request.Content?.Headers.ContentType;
var version = contentType is null ? default : ReadContentTypeHeader( contentType );
@@ -21,18 +18,18 @@ public virtual IReadOnlyList Read( HttpRequestMessage request )
if ( accept is null || ReadAcceptHeader( accept ) is not string otherVersion )
{
- return version is null ? Array.Empty() : new[] { version };
+ return version is null ? [] : [version];
}
var comparer = StringComparer.OrdinalIgnoreCase;
if ( version is null || comparer.Equals( version, otherVersion ) )
{
- return new[] { otherVersion };
+ return [otherVersion];
}
- return comparer.Compare( version, otherVersion ) <= 0 ?
- new[] { version, otherVersion } :
- new[] { otherVersion, version };
+ return comparer.Compare( version, otherVersion ) <= 0
+ ? [version, otherVersion]
+ : [otherVersion, version];
}
}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs
index 11f0fcb3..413f20e0 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs
@@ -20,27 +20,22 @@ public partial class MediaTypeApiVersionReaderBuilder
/// If a value is not specified, there is expected to be a single template parameter.
/// The current .
/// The template syntax is the same used by route templates; however, constraints are not supported.
-#pragma warning disable CA1716 // Identifiers should not match keywords
public virtual MediaTypeApiVersionReaderBuilder Template( string template, string? parameterName = default )
-#pragma warning restore CA1716 // Identifiers should not match keywords
{
- if ( string.IsNullOrEmpty( template ) )
- {
- throw new ArgumentNullException( nameof( template ) );
- }
+ ArgumentException.ThrowIfNullOrEmpty( template );
if ( string.IsNullOrEmpty( parameterName ) )
{
var parser = new RouteParser();
var parsedRoute = parser.Parse( template );
var segments = from content in parsedRoute.PathSegments.OfType()
- from segment in content.Subsegments.OfType()
- select segment;
+ from segment in content.Subsegments.OfType()
+ select segment;
if ( segments.Count() > 1 )
{
var message = string.Format( CultureInfo.CurrentCulture, CommonSR.InvalidMediaTypeTemplate, template );
- throw new ArgumentException( message, nameof( template ) );
+ throw new System.ArgumentException( message, nameof( template ) );
}
}
@@ -113,11 +108,6 @@ private static IReadOnlyList ReadMediaTypePattern(
}
}
- if ( version is null )
- {
- return Array.Empty();
- }
-
- return versions is null ? new[] { version } : versions.ToArray();
+ return ToArray( ref version, versions );
}
}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetails.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetails.cs
index b6a27396..e3640c0e 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetails.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetails.cs
@@ -32,7 +32,7 @@ public class ProblemDetails
/// (e.g., using HTML [W3C.REC-html5-20141028]). When this member is not present, its value is assumed to be
/// "about:blank".
///
- [JsonProperty( "type" )]
+ [JsonProperty( "type", NullValueHandling = Ignore )]
public string? Type { get; set; }
///
@@ -72,5 +72,6 @@ public class ProblemDetails
/// The round-tripping behavior for is determined by the implementation of the Input \ Output formatters.
/// In particular, complex types or collection types may not round-trip to the original type when using the built-in JSON or XML formatters.
///
+ [JsonExtensionData]
public IDictionary Extensions => extensions;
}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetailsFactory.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetailsFactory.cs
index 3bb429dd..baaad563 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetailsFactory.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetailsFactory.cs
@@ -10,11 +10,16 @@ namespace Asp.Versioning;
internal sealed class ProblemDetailsFactory : IProblemDetailsFactory
{
- private static ProblemDetailsFactory? @default;
-
- public static IProblemDetailsFactory Default => @default ??= new();
-
public ProblemDetails CreateProblemDetails(
+ HttpRequestMessage request,
+ int? statusCode = null,
+ string? title = null,
+ string? type = null,
+ string? detail = null,
+ string? instance = null ) =>
+ NewProblemDetails( request, statusCode, title, type, detail, instance );
+
+ internal static ProblemDetails NewProblemDetails(
HttpRequestMessage request,
int? statusCode = null,
string? title = null,
@@ -40,7 +45,7 @@ public ProblemDetails CreateProblemDetails(
else if ( type == Invalid.Type )
{
problemDetails.Code = Invalid.Code;
- return AddInvalidExtensions( request, status, problemDetails );
+ return AddInvalidExtensions( request, status, problemDetails, ApplyMessage );
}
else if ( type == Unspecified.Type )
{
@@ -49,13 +54,17 @@ public ProblemDetails CreateProblemDetails(
else if ( type == Unsupported.Type )
{
problemDetails.Code = Unsupported.Code;
- return AddUnsupportedExtensions( request, status, problemDetails );
+ return AddUnsupportedExtensions( request, status, problemDetails, ApplyMessage );
}
return problemDetails;
}
- private static ProblemDetailsEx AddInvalidExtensions( HttpRequestMessage request, int status, ProblemDetailsEx problemDetails )
+ internal static T AddInvalidExtensions(
+ HttpRequestMessage request,
+ int status,
+ T problemDetails,
+ Action applyMessage ) where T : ProblemDetails
{
if ( status != 400 || !request.ShouldIncludeErrorDetail() )
{
@@ -64,14 +73,18 @@ private static ProblemDetailsEx AddInvalidExtensions( HttpRequestMessage request
var safeUrl = request.RequestUri.SafeFullPath();
var requestedVersion = request.ApiVersionProperties().RawRequestedApiVersion;
- var error = string.Format( CurrentCulture, SR.VersionedControllerNameNotFound, safeUrl, requestedVersion );
+ var message = string.Format( CurrentCulture, SR.VersionedControllerNameNotFound, safeUrl, requestedVersion );
- problemDetails.Error = error;
+ applyMessage( problemDetails, message );
return problemDetails;
}
- private static ProblemDetailsEx AddUnsupportedExtensions( HttpRequestMessage request, int status, ProblemDetailsEx problemDetails )
+ internal static T AddUnsupportedExtensions(
+ HttpRequestMessage request,
+ int status,
+ T problemDetails,
+ Action applyMessage ) where T : ProblemDetails
{
if ( !request.ShouldIncludeErrorDetail() )
{
@@ -95,14 +108,17 @@ private static ProblemDetailsEx AddUnsupportedExtensions( HttpRequestMessage req
var safeUrl = request.RequestUri.SafeFullPath();
var requestedMethod = request.Method;
- var version = request.GetRequestedApiVersion()?.ToString() ?? "(null)";
- var error = string.Format( CurrentCulture, messageFormat, safeUrl, version, requestedMethod );
+ var version = request.ApiVersionProperties().RawRequestedApiVersion ?? "(null)";
+ var message = string.Format( CurrentCulture, messageFormat, safeUrl, version, requestedMethod );
- problemDetails.Error = error;
+ applyMessage( problemDetails, message );
return problemDetails;
}
+ private static void ApplyMessage( ProblemDetailsEx problemDetails, string message ) =>
+ problemDetails.Error = message;
+
private sealed class ProblemDetailsEx : ProblemDetails
{
[JsonProperty( "code", NullValueHandling = Ignore )]
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/QueryStringApiVersionReader.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/QueryStringApiVersionReader.cs
index 3f119177..8b88062b 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/QueryStringApiVersionReader.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/QueryStringApiVersionReader.cs
@@ -10,10 +10,7 @@ public partial class QueryStringApiVersionReader
///
public virtual IReadOnlyList Read( HttpRequestMessage request )
{
- if ( request == null )
- {
- throw new ArgumentNullException( nameof( request ) );
- }
+ ArgumentNullException.ThrowIfNull(request);
var count = ParameterNames.Count;
@@ -62,7 +59,7 @@ public virtual IReadOnlyList Read( HttpRequestMessage request )
if ( versions == null )
{
- return version == null ? Array.Empty() : new[] { version };
+ return version == null ? [] : [version];
}
return versions.ToArray();
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt
index 40001e18..5f282702 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt
@@ -1 +1 @@
-Fix MediaTypeApiVersionReaderBuilder.AddParameters (#904)
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReportApiVersionsAttribute.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReportApiVersionsAttribute.cs
index 8208fd7e..782118fa 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReportApiVersionsAttribute.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReportApiVersionsAttribute.cs
@@ -18,10 +18,7 @@ public sealed partial class ReportApiVersionsAttribute
/// response provided that there is a response and the executed action was not version-neutral.
public override void OnActionExecuted( HttpActionExecutedContext actionExecutedContext )
{
- if ( actionExecutedContext == null )
- {
- throw new ArgumentNullException( nameof( actionExecutedContext ) );
- }
+ ArgumentNullException.ThrowIfNull( actionExecutedContext );
var response = actionExecutedContext.Response;
@@ -32,14 +29,7 @@ public override void OnActionExecuted( HttpActionExecutedContext actionExecutedC
var context = actionExecutedContext.ActionContext;
var action = context.ActionDescriptor;
- var reporter = reportApiVersions;
-
- if ( reporter is null )
- {
- var dependencyResolver = context.ControllerContext.Configuration.DependencyResolver;
- reporter = dependencyResolver.GetApiVersionReporter();
- }
-
+ var reporter = reportApiVersions ?? context.ControllerContext.Configuration.GetApiVersionReporter();
var model = action.GetApiVersionMetadata().Map( reporter.Mapping );
response.RequestMessage ??= actionExecutedContext.Request;
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs
index 3af05991..ad930d65 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs
@@ -2,7 +2,6 @@
namespace Asp.Versioning.Routing;
-using System.Web.Http;
using System.Web.Http.Routing;
///
@@ -21,17 +20,14 @@ public sealed class ApiVersionRouteConstraint : IHttpRouteConstraint
/// True if the route constraint is matched; otherwise, false.
public bool Match( HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection )
{
- if ( values == null )
- {
- throw new ArgumentNullException( nameof( values ) );
- }
+ ArgumentNullException.ThrowIfNull( values );
if ( string.IsNullOrEmpty( parameterName ) )
{
return false;
}
- if ( !values.TryGetValue( parameterName, out string value ) )
+ if ( !values.TryGetValue( parameterName, out string? value ) )
{
return false;
}
@@ -41,7 +37,7 @@ public bool Match( HttpRequestMessage request, IHttpRoute route, string paramete
return !string.IsNullOrEmpty( value );
}
- var parser = request.GetConfiguration().DependencyResolver.GetApiVersionParser();
+ var parser = request.GetConfiguration().GetApiVersionParser();
var properties = request.ApiVersionProperties();
properties.RouteParameter = parameterName;
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionUrlHelper.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionUrlHelper.cs
index cb414b6e..8a1beea4 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionUrlHelper.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionUrlHelper.cs
@@ -15,7 +15,7 @@ public class ApiVersionUrlHelper : UrlHelper
/// The inner URL helper .
public ApiVersionUrlHelper( UrlHelper url )
{
- Url = url ?? throw new ArgumentNullException( nameof( url ) );
+ Url = url ?? throw new System.ArgumentNullException( nameof( url ) );
if ( url.Request != null )
{
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/BoundRouteTemplateAdapter{T}.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/BoundRouteTemplateAdapter{T}.cs
index a5c6446b..8d5de8fb 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/BoundRouteTemplateAdapter{T}.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/BoundRouteTemplateAdapter{T}.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1812
-
namespace Asp.Versioning.Routing;
using System.Web.Http.Routing;
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IBoundRouteTemplate.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IBoundRouteTemplate.cs
index 6bd7dced..c2ce56f5 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IBoundRouteTemplate.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IBoundRouteTemplate.cs
@@ -15,11 +15,9 @@ public interface IBoundRouteTemplate
/// The bound template.
string BoundTemplate { get; set; }
-#pragma warning disable CA2227 // Collection properties should be read only
///
/// Gets or sets the template parameter values.
///
/// The template route value dictionary .
HttpRouteValueDictionary Values { get; set; }
-#pragma warning restore CA2227
}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSegment.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSegment.cs
index 5a57da6d..d6e6e455 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSegment.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSegment.cs
@@ -2,8 +2,6 @@
namespace Asp.Versioning.Routing;
-#pragma warning disable CA1040 // Avoid empty interfaces
-
///
/// Defines the behavior of a path segment.
///
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSeparatorSegment.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSeparatorSegment.cs
index d57a4a7b..ca2829d6 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSeparatorSegment.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSeparatorSegment.cs
@@ -2,8 +2,6 @@
namespace Asp.Versioning.Routing;
-#pragma warning disable CA1040 // Avoid empty interfaces
-
///
/// Defines the behavior of a path separator.
///
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSubsegment.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSubsegment.cs
index 8a180b7c..49eec855 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSubsegment.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSubsegment.cs
@@ -2,8 +2,6 @@
namespace Asp.Versioning.Routing;
-#pragma warning disable CA1040 // Avoid empty interfaces
-
///
/// Defines the behavior of a path subsegment.
///
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ParsedRouteAdapter{T}.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ParsedRouteAdapter{T}.cs
index 28148ce3..76ac7302 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ParsedRouteAdapter{T}.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ParsedRouteAdapter{T}.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1812
-
namespace Asp.Versioning.Routing;
using System.Reflection;
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathContentSegmentAdapter{T}.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathContentSegmentAdapter{T}.cs
index 77f350fa..7f62ae53 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathContentSegmentAdapter{T}.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathContentSegmentAdapter{T}.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1812
-
namespace Asp.Versioning.Routing;
using static System.Linq.Expressions.Expression;
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathLiteralSubsegmentAdapter{T}.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathLiteralSubsegmentAdapter{T}.cs
index 65e1bd05..a565ba0e 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathLiteralSubsegmentAdapter{T}.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathLiteralSubsegmentAdapter{T}.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1812
-
namespace Asp.Versioning.Routing;
using static System.Linq.Expressions.Expression;
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathParameterSubsegmentAdapter{T}.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathParameterSubsegmentAdapter{T}.cs
index f51c711c..5250bfe1 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathParameterSubsegmentAdapter{T}.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathParameterSubsegmentAdapter{T}.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1812
-
namespace Asp.Versioning.Routing;
using static System.Linq.Expressions.Expression;
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathSeparatorSegmentAdapter{T}.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathSeparatorSegmentAdapter{T}.cs
index 9c16ab59..4d94a78d 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathSeparatorSegmentAdapter{T}.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathSeparatorSegmentAdapter{T}.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1812
-
namespace Asp.Versioning.Routing;
internal sealed class PathSeparatorSegmentAdapter : IPathSeparatorSegment where T : notnull
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/UrlHelperExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/UrlHelperExtensions.cs
index a53f0f3e..121d6e75 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/UrlHelperExtensions.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/UrlHelperExtensions.cs
@@ -3,6 +3,7 @@
namespace System.Web.Http.Routing;
using Asp.Versioning.Routing;
+using Backport;
///
/// Provides extension methods for .
@@ -22,10 +23,7 @@ public static class UrlHelperExtensions
/// it would be erroneously added as a query string parameter.
public static UrlHelper WithoutApiVersion( this UrlHelper urlHelper )
{
- if ( urlHelper == null )
- {
- throw new ArgumentNullException( nameof( urlHelper ) );
- }
+ ArgumentNullException.ThrowIfNull( urlHelper );
if ( urlHelper is WithoutApiVersionUrlHelper )
{
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs
index 56042d8b..c272abc8 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs
@@ -114,6 +114,15 @@ internal static string ApiVersionUnspecified {
}
}
+ ///
+ /// Looks up a localized string similar to The value cannot be an empty string..
+ ///
+ internal static string Argument_EmptyString {
+ get {
+ return ResourceManager.GetString("Argument_EmptyString", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to No route providing a controller name was found to match request URI '{0}'..
///
@@ -159,6 +168,15 @@ internal static string DirectRoute_AmbiguousController {
}
}
+ ///
+ /// Looks up a localized string similar to {0}.{1} is an invalid value for {2}.{3}. Did you mean to apply {4} via attribute or convention instead?.
+ ///
+ internal static string InvalidDefaultApiVersion {
+ get {
+ return ResourceManager.GetString("InvalidDefaultApiVersion", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to A controller was not selected for request URI '{0}' and API version '{1}'..
///
@@ -168,6 +186,15 @@ internal static string NoControllerSelected {
}
}
+ ///
+ /// Looks up a localized string similar to No service for type '{0}' has been registered..
+ ///
+ internal static string NoServiceRegistered {
+ get {
+ return ResourceManager.GetString("NoServiceRegistered", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to No HTTP resource was found that matches the request URI '{0}'..
///
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx
index ed143dc1..dced0e03 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx
@@ -135,6 +135,9 @@
An API version is required, but was not specified.
+
+ The value cannot be an empty string.
+
No route providing a controller name was found to match request URI '{0}'.
@@ -150,9 +153,20 @@
Multiple controller types were found that match the URL. This can happen if attribute routes on multiple controllers match the requested URL.{1}{1}The request has found the following matching controller types: {0}
+
+ {0}.{1} is an invalid value for {2}.{3}. Did you mean to apply {4} via attribute or convention instead?
+ 0 = ApiVersion
+1 = Neutral
+2 = ApiVersioningOptions
+3 = DefaultApiVersion
+4 = IApiVersionNeutral
+
A controller was not selected for request URI '{0}' and API version '{1}'.
+
+ No service for type '{0}' has been registered.
+
No HTTP resource was found that matches the request URI '{0}'.
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SunsetPolicyManager.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SunsetPolicyManager.cs
index b15d2160..78af0cdf 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SunsetPolicyManager.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SunsetPolicyManager.cs
@@ -8,17 +8,10 @@ namespace Asp.Versioning;
public partial class SunsetPolicyManager
{
private readonly ApiVersioningOptions options;
- private static ISunsetPolicyManager? @default;
///
/// Initializes a new instance of the class.
///
/// The associated API versioning options .
public SunsetPolicyManager( ApiVersioningOptions options ) => this.options = options;
-
- internal static ISunsetPolicyManager Default
- {
- get => @default ?? new SunsetPolicyManager( new ApiVersioningOptions() );
- set => @default = value;
- }
}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs
index 25abca3a..e6163773 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs
@@ -3,6 +3,7 @@
namespace System.Net.Http;
using Asp.Versioning;
+using Backport;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Web.Http;
@@ -22,9 +23,7 @@ private static HttpResponseMessage CreateErrorResponse( this HttpRequestMessage
if ( configuration == null )
{
-#pragma warning disable CA2000 // Dispose objects before losing scope
configuration = new HttpConfiguration();
-#pragma warning restore CA2000 // Dispose objects before losing scope
request.RegisterForDispose( configuration );
request.SetConfiguration( configuration );
}
@@ -60,9 +59,7 @@ public static ApiVersioningOptions GetApiVersioningOptions( this HttpRequestMess
if ( configuration == null )
{
-#pragma warning disable CA2000 // Dispose objects before losing scope
configuration = new HttpConfiguration();
-#pragma warning restore CA2000 // Dispose objects before losing scope
request.RegisterForDispose( configuration );
request.SetConfiguration( configuration );
}
@@ -77,14 +74,11 @@ public static ApiVersioningOptions GetApiVersioningOptions( this HttpRequestMess
/// The current API versioning properties .
public static ApiVersionRequestProperties ApiVersionProperties( this HttpRequestMessage request )
{
- if ( request == null )
- {
- throw new ArgumentNullException( nameof( request ) );
- }
+ ArgumentNullException.ThrowIfNull( request );
- if ( request.Properties.TryGetValue( ApiVersionPropertiesKey, out ApiVersionRequestProperties properties ) )
+ if ( request.Properties.TryGetValue( ApiVersionPropertiesKey, out ApiVersionRequestProperties? properties ) )
{
- return properties;
+ return properties!;
}
var forceRouteConstraintEvaluation = !request.Properties.ContainsKey( RoutingContextKey );
@@ -126,14 +120,19 @@ internal static Tuple GetProblemDetail
{
var configuration = request.GetConfiguration();
var negotiator = configuration.Services.GetContentNegotiator();
- var result = negotiator.Negotiate( typeof( ProblemDetails ), request, configuration.Formatters ) ??
- new( configuration.Formatters.JsonFormatter ?? new(), MediaTypeHeaderValue.Parse( "application/problem+json" ) );
+ var result = negotiator.Negotiate( typeof( ProblemDetails ), request, configuration.Formatters );
return result.MediaType.MediaType switch
{
- "application/json" => Tuple.Create( MediaTypeHeaderValue.Parse( "application/problem+json" ), result.Formatter ),
- "application/xml" => Tuple.Create( MediaTypeHeaderValue.Parse( "application/problem+xml" ), result.Formatter ),
- _ => Tuple.Create( result.MediaType, result.Formatter ),
+ null => Tuple.Create(
+ MediaTypeHeaderValue.Parse( ProblemDetailsDefaults.MediaType.Json ),
+ (MediaTypeFormatter) ( configuration.Formatters.JsonFormatter ?? new() ) ),
+ "application/xml" => Tuple.Create(
+ MediaTypeHeaderValue.Parse( ProblemDetailsDefaults.MediaType.Xml ),
+ result.Formatter ),
+ _ => Tuple.Create(
+ result.MediaType,
+ result.Formatter ),
};
}
}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpResponseMessageExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpResponseMessageExtensions.cs
index 4b9d2354..2f9e1510 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpResponseMessageExtensions.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpResponseMessageExtensions.cs
@@ -3,7 +3,7 @@
namespace System.Net.Http;
using Asp.Versioning;
-using System;
+using Backport;
using System.Collections.Generic;
using System.Net.Http.Headers;
@@ -22,15 +22,8 @@ public static class HttpResponseMessageExtensions
/// The sunset policy to write.
public static void WriteSunsetPolicy( this HttpResponseMessage response, SunsetPolicy sunsetPolicy )
{
- if ( response == null )
- {
- throw new ArgumentNullException( nameof( response ) );
- }
-
- if ( sunsetPolicy == null )
- {
- throw new ArgumentNullException( nameof( sunsetPolicy ) );
- }
+ ArgumentNullException.ThrowIfNull( response );
+ ArgumentNullException.ThrowIfNull( sunsetPolicy );
var headers = response.Headers;
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpActionDescriptorExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpActionDescriptorExtensions.cs
index 0821d758..77a1f230 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpActionDescriptorExtensions.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpActionDescriptorExtensions.cs
@@ -3,6 +3,7 @@
namespace System.Web.Http;
using Asp.Versioning;
+using Backport;
using System.ComponentModel;
using System.Web.Http.Controllers;
@@ -20,14 +21,11 @@ public static class HttpActionDescriptorExtensions
/// The API version information for the action.
public static ApiVersionMetadata GetApiVersionMetadata( this HttpActionDescriptor action )
{
- if ( action == null )
- {
- throw new ArgumentNullException( nameof( action ) );
- }
+ ArgumentNullException.ThrowIfNull( action );
- if ( action.Properties.TryGetValue( typeof( ApiVersionMetadata ), out ApiVersionMetadata value ) )
+ if ( action.Properties.TryGetValue( typeof( ApiVersionMetadata ), out ApiVersionMetadata? value ) )
{
- return value;
+ return value!;
}
return ApiVersionMetadata.Empty;
@@ -42,11 +40,7 @@ public static ApiVersionMetadata GetApiVersionMetadata( this HttpActionDescripto
[EditorBrowsable( EditorBrowsableState.Never )]
public static void SetApiVersionMetadata( this HttpActionDescriptor action, ApiVersionMetadata value )
{
- if ( action == null )
- {
- throw new ArgumentNullException( nameof( action ) );
- }
-
+ ArgumentNullException.ThrowIfNull( action );
action.Properties.AddOrUpdate( typeof( ApiVersionMetadata ), value, ( key, oldValue ) => value );
}
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs
index 932639c4..0051de64 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs
@@ -4,7 +4,11 @@ namespace System.Web.Http;
using Asp.Versioning;
using Asp.Versioning.Controllers;
+using Asp.Versioning.Dependencies;
using Asp.Versioning.Dispatcher;
+using Asp.Versioning.Formatting;
+using Backport;
+using System.Globalization;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using static Asp.Versioning.ApiVersionParameterLocation;
@@ -14,7 +18,7 @@ namespace System.Web.Http;
///
public static class HttpConfigurationExtensions
{
- private const string ApiVersioningOptionsKey = "MS_ApiVersioningOptions";
+ private const string ApiVersioningServicesKey = "MS_ApiVersioningServices";
///
/// Gets the current API versioning options.
@@ -23,12 +27,22 @@ public static class HttpConfigurationExtensions
/// The current API versioning options .
public static ApiVersioningOptions GetApiVersioningOptions( this HttpConfiguration configuration )
{
- if ( configuration == null )
- {
- throw new ArgumentNullException( nameof( configuration ) );
- }
+ ArgumentNullException.ThrowIfNull( configuration );
+ return configuration.ApiVersioningServices().ApiVersioningOptions;
+ }
- return (ApiVersioningOptions) configuration.Properties.GetOrAdd( ApiVersioningOptionsKey, key => new ApiVersioningOptions() );
+ ///
+ /// Converts problem details into error objects.
+ ///
+ /// The current configuration .
+ /// This enables backward compatibility by converting into Error Objects that
+ /// conform to the Error Responses
+ /// in the Microsoft REST API Guidelines and
+ /// OData Error Responses .
+ public static void ConvertProblemDetailsToErrorObject( this HttpConfiguration configuration )
+ {
+ ArgumentNullException.ThrowIfNull( configuration );
+ configuration.Initializer += EnableErrorObjectResponses;
}
///
@@ -37,11 +51,7 @@ public static ApiVersioningOptions GetApiVersioningOptions( this HttpConfigurati
/// The configuration that will use service versioning.
public static void AddApiVersioning( this HttpConfiguration configuration )
{
- if ( configuration == null )
- {
- throw new ArgumentNullException( nameof( configuration ) );
- }
-
+ ArgumentNullException.ThrowIfNull( configuration );
configuration.AddApiVersioning( new ApiVersioningOptions() );
}
@@ -52,19 +62,13 @@ public static void AddApiVersioning( this HttpConfiguration configuration )
/// An action used to configure the provided options.
public static void AddApiVersioning( this HttpConfiguration configuration, Action setupAction )
{
- if ( configuration == null )
- {
- throw new ArgumentNullException( nameof( configuration ) );
- }
-
- if ( setupAction == null )
- {
- throw new ArgumentNullException( nameof( setupAction ) );
- }
+ ArgumentNullException.ThrowIfNull( configuration );
+ ArgumentNullException.ThrowIfNull( setupAction );
var options = new ApiVersioningOptions();
setupAction( options );
+ ValidateApiVersioningOptions( options );
configuration.AddApiVersioning( options );
}
@@ -92,20 +96,59 @@ private static void AddApiVersioning( this HttpConfiguration configuration, ApiV
}
}
- configuration.Properties.AddOrUpdate( ApiVersioningOptionsKey, options, ( key, oldValue ) => options );
+ configuration.ApiVersioningServices().ApiVersioningOptions = options;
configuration.ParameterBindingRules.Add( typeof( ApiVersion ), ApiVersionParameterBinding.Create );
- SunsetPolicyManager.Default = new SunsetPolicyManager( options );
+ configuration.Formatters.Insert( 0, new ProblemDetailsMediaTypeFormatter( configuration.Formatters.JsonFormatter ?? new() ) );
}
- internal static IReportApiVersions GetApiVersionReporter( this HttpConfiguration configuration )
+ // ApiVersion.Neutral does not have the same meaning as IApiVersionNeutral. setting
+ // ApiVersioningOptions.DefaultApiVersion this value will not make all APIs version-neutral
+ // and will likely lead to many unexpected side effects. this is a best-effort, one-time
+ // validation check to help prevent people from going off the rails. if someone bypasses
+ // this validation by removing the check or updating the value later, then caveat emptor.
+ //
+ // REF: https://github.com/dotnet/aspnet-api-versioning/issues/1011
+ private static void ValidateApiVersioningOptions( ApiVersioningOptions options )
{
- var options = configuration.GetApiVersioningOptions();
-
- if ( options.ReportApiVersions )
+ if ( options.DefaultApiVersion == ApiVersion.Neutral )
{
- return configuration.DependencyResolver.GetApiVersionReporter();
+ var message = string.Format(
+ CultureInfo.CurrentCulture,
+ SR.InvalidDefaultApiVersion,
+ nameof( ApiVersion ),
+ nameof( ApiVersion.Neutral ),
+ nameof( ApiVersioningOptions ),
+ nameof( ApiVersioningOptions.DefaultApiVersion ),
+ nameof( IApiVersionNeutral ) );
+
+ throw new InvalidOperationException( message );
}
+ }
+
+ private static void EnableErrorObjectResponses( HttpConfiguration configuration )
+ {
+ configuration.ApiVersioningServices().Replace(
+ typeof( IProblemDetailsFactory ),
+ static ( sc, t ) => new ErrorObjectFactory() );
+
+ var formatters = configuration.Formatters;
+ var problemDetails = ProblemDetailsMediaTypeFormatter.DefaultMediaType;
- return DoNotReportApiVersions.Instance;
+ for ( var i = 0; i < formatters.Count; i++ )
+ {
+ var mediaTypes = formatters[i].SupportedMediaTypes;
+
+ for ( var j = 0; j < mediaTypes.Count; j++ )
+ {
+ if ( mediaTypes[j].Equals( problemDetails ) )
+ {
+ formatters.RemoveAt( i );
+ return;
+ }
+ }
+ }
}
+
+ internal static DefaultContainer ApiVersioningServices( this HttpConfiguration configuration ) =>
+ (DefaultContainer) configuration.Properties.GetOrAdd( ApiVersioningServicesKey, key => new DefaultContainer() );
}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpControllerDescriptorExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpControllerDescriptorExtensions.cs
index 3e66c57d..8efe2d8c 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpControllerDescriptorExtensions.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpControllerDescriptorExtensions.cs
@@ -4,6 +4,7 @@ namespace System.Web.Http;
using Asp.Versioning;
using Asp.Versioning.Controllers;
+using Backport;
using System.ComponentModel;
using System.Web.Http.Controllers;
using System.Web.Http.Description;
@@ -34,14 +35,11 @@ public static class HttpControllerDescriptorExtensions
[EditorBrowsable( EditorBrowsableState.Never )]
public static ApiVersionModel GetApiVersionModel( this HttpControllerDescriptor controllerDescriptor )
{
- if ( controllerDescriptor == null )
- {
- throw new ArgumentNullException( nameof( controllerDescriptor ) );
- }
+ ArgumentNullException.ThrowIfNull( controllerDescriptor );
- if ( controllerDescriptor.Properties.TryGetValue( typeof( ApiVersionModel ), out ApiVersionModel value ) )
+ if ( controllerDescriptor.Properties.TryGetValue( typeof( ApiVersionModel ), out ApiVersionModel? value ) )
{
- return value;
+ return value!;
}
return ApiVersionModel.Empty;
@@ -56,10 +54,7 @@ public static ApiVersionModel GetApiVersionModel( this HttpControllerDescriptor
[EditorBrowsable( EditorBrowsableState.Never )]
public static void SetApiVersionModel( this HttpControllerDescriptor controllerDescriptor, ApiVersionModel value )
{
- if ( controllerDescriptor == null )
- {
- throw new ArgumentNullException( nameof( controllerDescriptor ) );
- }
+ ArgumentNullException.ThrowIfNull( controllerDescriptor );
controllerDescriptor.Properties.AddOrUpdate( typeof( ApiVersionModel ), value, ( key, oldValue ) => value );
@@ -84,10 +79,7 @@ public static IEnumerable AsEnumerable( this HttpContr
internal static IEnumerable AsEnumerable( this HttpControllerDescriptor controllerDescriptor, bool includeCandidates )
{
- if ( controllerDescriptor == null )
- {
- throw new ArgumentNullException( nameof( controllerDescriptor ) );
- }
+ ArgumentNullException.ThrowIfNull( controllerDescriptor );
var visited = new HashSet();
@@ -107,12 +99,12 @@ internal static IEnumerable AsEnumerable( this HttpCon
yield return controllerDescriptor;
}
- if ( !includeCandidates || !controllerDescriptor.Properties.TryGetValue( PossibleControllerCandidatesKey, out IEnumerable candidates ) )
+ if ( !includeCandidates || !controllerDescriptor.Properties.TryGetValue( PossibleControllerCandidatesKey, out IEnumerable? candidates ) )
{
yield break;
}
- foreach ( var candidate in candidates )
+ foreach ( var candidate in candidates! )
{
if ( visited.Add( candidate ) )
{
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteCollectionExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteCollectionExtensions.cs
index d2d3a7c1..64310188 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteCollectionExtensions.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteCollectionExtensions.cs
@@ -2,6 +2,7 @@
namespace System.Web.Http;
+using Backport;
using System.Reflection;
using System.Web.Http.Routing;
using static System.Reflection.BindingFlags;
@@ -19,10 +20,7 @@ public static class HttpRouteCollectionExtensions
/// routes mapped to their name.
public static IReadOnlyDictionary ToDictionary( this HttpRouteCollection routes )
{
- if ( routes == null )
- {
- throw new ArgumentNullException( nameof( routes ) );
- }
+ ArgumentNullException.ThrowIfNull( routes );
const string HostedHttpRouteCollection = "System.Web.Http.WebHost.Routing.HostedHttpRouteCollection";
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteDataExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteDataExtensions.cs
index 20d6990e..314c9bc9 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteDataExtensions.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteDataExtensions.cs
@@ -33,6 +33,6 @@ internal static class HttpRouteDataExtensions
}
}
- return list.ToArray();
+ return [.. list];
}
}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteExtensions.cs
index 5c69c15c..9971270b 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteExtensions.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteExtensions.cs
@@ -20,7 +20,7 @@ internal static class HttpRouteExtensions
var directRouteActions = default( HttpActionDescriptor[] );
- if ( dataTokens.TryGetValue( RouteDataTokenKeys.Actions, out HttpActionDescriptor[] possibleDirectRouteActions ) &&
+ if ( dataTokens.TryGetValue( RouteDataTokenKeys.Actions, out HttpActionDescriptor[]? possibleDirectRouteActions ) &&
possibleDirectRouteActions != null &&
possibleDirectRouteActions.Length > 0 )
{
@@ -49,6 +49,6 @@ internal static class HttpRouteExtensions
candidates.Add( new CandidateAction( directRouteActions[i], order, precedence ) );
}
- return candidates.ToArray();
+ return [.. candidates];
}
}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/UrlSegmentApiVersionReader.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/UrlSegmentApiVersionReader.cs
index 04e31809..5cfa1e1e 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/UrlSegmentApiVersionReader.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/UrlSegmentApiVersionReader.cs
@@ -11,10 +11,7 @@ public partial class UrlSegmentApiVersionReader
///
public virtual IReadOnlyList Read( HttpRequestMessage request )
{
- if ( request == null )
- {
- throw new ArgumentNullException( nameof( request ) );
- }
+ ArgumentNullException.ThrowIfNull(request);
if ( reentrant )
{
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/ApiExplorer/VersionedApiExplorerTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/ApiExplorer/VersionedApiExplorerTest.cs
index e42d2651..42c21028 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/ApiExplorer/VersionedApiExplorerTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/ApiExplorer/VersionedApiExplorerTest.cs
@@ -1,7 +1,10 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+//// Ignore Spelling: Dcase
+//// Ignore Spelling: Dinsensitive
namespace Asp.Versioning.ApiExplorer;
+
using Asp.Versioning.Models;
using Asp.Versioning.Routing;
using Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiDescriptionGroupCollectionTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiDescriptionGroupCollectionTest.cs
index 55dc4e1d..bca97b12 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiDescriptionGroupCollectionTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiDescriptionGroupCollectionTest.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+//// Ignore Spelling: denormalized
+
namespace Asp.Versioning.Description;
using System.Collections.ObjectModel;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiVersionParameterDescriptionContextTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiVersionParameterDescriptionContextTest.cs
index dca4f3b8..d2fb7be2 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiVersionParameterDescriptionContextTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiVersionParameterDescriptionContextTest.cs
@@ -4,13 +4,11 @@ namespace Asp.Versioning.Description;
using Asp.Versioning.ApiExplorer;
using Asp.Versioning.Routing;
-using System.Collections.ObjectModel;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Description;
-using System.Web.Http.Filters;
using System.Web.Http.Routing;
using static Asp.Versioning.ApiVersionParameterLocation;
using static System.Web.Http.Description.ApiParameterSource;
@@ -234,11 +232,19 @@ public void add_parameter_should_add_optional_parameter_when_allowed()
var configuration = new HttpConfiguration();
var action = NewActionDescriptor();
var description = new ApiDescription() { ActionDescriptor = action };
- var version = new ApiVersion( 1, 0 );
- var options = new ApiExplorerOptions( configuration );
+ var version = new ApiVersion( 2.0 );
+ var options = new ApiExplorerOptions( configuration )
+ {
+ ApiVersionSelector = new ConstantApiVersionSelector( version ),
+ };
action.Configuration = configuration;
- configuration.AddApiVersioning( o => o.AssumeDefaultVersionWhenUnspecified = true );
+ configuration.AddApiVersioning(
+ options =>
+ {
+ options.DefaultApiVersion = ApiVersion.Default;
+ options.AssumeDefaultVersionWhenUnspecified = true;
+ } );
var context = new ApiVersionParameterDescriptionContext( description, version, options );
@@ -255,7 +261,7 @@ public void add_parameter_should_add_optional_parameter_when_allowed()
ParameterDescriptor = new
{
ParameterName = "api-version",
- DefaultValue = "1.0",
+ DefaultValue = "2.0",
IsOptional = true,
Configuration = configuration,
ActionDescriptor = action,
@@ -291,9 +297,9 @@ private static HttpActionDescriptor NewActionDescriptor()
var action = new Mock() { CallBase = true }.Object;
var controller = new Mock() { CallBase = true };
- controller.Setup( c => c.GetCustomAttributes( It.IsAny() ) ).Returns( new Collection() );
- controller.Setup( c => c.GetCustomAttributes( It.IsAny() ) ).Returns( new Collection() );
- controller.Setup( c => c.GetFilters() ).Returns( new Collection() );
+ controller.Setup( c => c.GetCustomAttributes( It.IsAny() ) ).Returns( [] );
+ controller.Setup( c => c.GetCustomAttributes( It.IsAny() ) ).Returns( [] );
+ controller.Setup( c => c.GetFilters() ).Returns( [] );
action.ControllerDescriptor = controller.Object;
return action;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/InternalTypeExtensions.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/InternalTypeExtensions.cs
index b2458e22..4dd18890 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/InternalTypeExtensions.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/InternalTypeExtensions.cs
@@ -20,9 +20,9 @@ internal static void EnsureInitialized( this IHttpRoute route, Func actions, bool targetIsAction )
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/GenericMutableObject{T}.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/GenericMutableObject{T}.cs
index db85d146..98fc3dbd 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/GenericMutableObject{T}.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/GenericMutableObject{T}.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+//// Ignore Spelling: Foo
+
namespace Asp.Versioning.Models;
public class GenericMutableObject : List
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/MutableObject.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/MutableObject.cs
index d5b0daaf..cef08ef1 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/MutableObject.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/MutableObject.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+//// Ignore Spelling: Foo
+
namespace Asp.Versioning.Models;
public class MutableObject
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/ApiExplorerValuesController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/ApiExplorerValuesController.cs
index 9a0218d1..c75b460b 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/ApiExplorerValuesController.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/ApiExplorerValuesController.cs
@@ -1,6 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeApiExplorerValuesController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeApiExplorerValuesController.cs
index 7e156d55..62240c77 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeApiExplorerValuesController.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeApiExplorerValuesController.cs
@@ -1,6 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues2Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues2Controller.cs
index a7bb3f20..709b16db 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues2Controller.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues2Controller.cs
@@ -1,8 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
#pragma warning disable IDE0060
-#pragma warning disable CA1062 // Validate arguments of public methods
-#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues3Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues3Controller.cs
index d25f7216..1c17d4bf 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues3Controller.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues3Controller.cs
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
#pragma warning disable IDE0060
-#pragma warning disable CA1062 // Validate arguments of public methods
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/DuplicatedIdController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/DuplicatedIdController.cs
index f6b40d55..1c401fc1 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/DuplicatedIdController.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/DuplicatedIdController.cs
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
#pragma warning disable IDE0060
-#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/IgnoreApiValuesController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/IgnoreApiValuesController.cs
index 734d9990..2107688b 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/IgnoreApiValuesController.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/IgnoreApiValuesController.cs
@@ -1,6 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values2Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values2Controller.cs
index a1b8b3b5..796aba7c 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values2Controller.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values2Controller.cs
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-#pragma warning disable CA1822 // Mark members as static
+#pragma warning disable IDE0060
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values3Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values3Controller.cs
index 69e8b09a..04f09f1c 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values3Controller.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values3Controller.cs
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
#pragma warning disable IDE0060
-#pragma warning disable CA1062 // Validate arguments of public methods
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Controllers/HttpControllerDescriptorGroupTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Controllers/HttpControllerDescriptorGroupTest.cs
index 2d4227c3..0b9ba137 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Controllers/HttpControllerDescriptorGroupTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Controllers/HttpControllerDescriptorGroupTest.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+//// Ignore Spelling: eq
+
namespace Asp.Versioning.Controllers;
using System.Collections.ObjectModel;
@@ -51,12 +53,12 @@ public void get_custom_attributes_should_aggregate_attributes()
var configuration = new HttpConfiguration();
descriptor1.Setup( d => d.GetCustomAttributes( It.IsAny() ) )
- .Returns( () => new Collection() { new ApiVersionAttribute( "1.0" ) } );
+ .Returns( () => new Collection() { new( "1.0" ) } );
descriptor1.Object.Configuration = configuration;
descriptor1.Object.Properties[typeof( ApiVersionModel )] = new ApiVersionModel( new ApiVersion( 1, 0 ) );
descriptor2.Setup( d => d.GetCustomAttributes( It.IsAny() ) )
- .Returns( () => new Collection() { new ApiVersionAttribute( "2.0" ) } );
+ .Returns( () => new Collection() { new( "2.0" ) } );
descriptor2.Object.Configuration = configuration;
descriptor2.Object.Properties[typeof( ApiVersionModel )] = new ApiVersionModel( new ApiVersion( 2, 0 ) );
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs
index d53ccda7..f99b03cf 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs
@@ -2,7 +2,6 @@
namespace Asp.Versioning.Conventions;
-using System.Collections.ObjectModel;
using System.Web.Http;
using System.Web.Http.Controllers;
using static Asp.Versioning.ApiVersionMapping;
@@ -17,7 +16,7 @@ public void apply_to_should_assign_empty_model_without_api_versions_from_mapped_
var actionBuilder = new ActionApiVersionConventionBuilder( controllerBuilder );
var actionDescriptor = new Mock() { CallBase = true };
- actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( new Collection() );
+ actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( [] );
actionDescriptor.Object.ControllerDescriptor = new();
// act
@@ -43,7 +42,7 @@ public void apply_to_should_assign_model_with_declared_api_versions_from_mapped_
var actionBuilder = new ActionApiVersionConventionBuilder( controllerBuilder );
var actionDescriptor = new Mock() { CallBase = true };
- actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( new Collection() );
+ actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( [] );
actionDescriptor.Object.ControllerDescriptor = new();
actionBuilder.MapToApiVersion( new ApiVersion( 2, 0 ) );
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs
index 12a5f6ee..e7626764 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs
@@ -2,7 +2,6 @@
namespace Asp.Versioning.Conventions;
-using System.Collections.ObjectModel;
using System.Web.Http;
using System.Web.Http.Controllers;
using static Asp.Versioning.ApiVersionMapping;
@@ -17,7 +16,7 @@ public void apply_to_should_assign_empty_model_without_api_versions_from_mapped_
var actionBuilder = new ActionApiVersionConventionBuilder( controllerBuilder );
var actionDescriptor = new Mock() { CallBase = true };
- actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( new Collection() );
+ actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( [] );
actionDescriptor.Object.ControllerDescriptor = new();
// act
@@ -43,7 +42,7 @@ public void apply_to_should_assign_model_with_declared_api_versions_from_mapped_
var actionBuilder = new ActionApiVersionConventionBuilder( controllerBuilder );
var actionDescriptor = new Mock() { CallBase = true };
- actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( new Collection() );
+ actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( [] );
actionDescriptor.Object.ControllerDescriptor = new();
actionBuilder.MapToApiVersion( new ApiVersion( 2, 0 ) );
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs
index e2fc1c6c..82164eb5 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs
@@ -19,7 +19,7 @@ public void apply_to_should_assign_conventions_to_controller()
var controllerDescriptor = mock.Object;
var controllerBuilder = default( IControllerConventionBuilder );
- mock.Setup( cd => cd.GetCustomAttributes() ).Returns( new Collection() );
+ mock.Setup( cd => cd.GetCustomAttributes() ).Returns( [] );
controllerDescriptor.Configuration = configuration;
controllerDescriptor.ControllerType = typeof( UndecoratedController );
configuration.AddApiVersioning( o => controllerBuilder = o.Conventions.Controller() );
@@ -54,7 +54,7 @@ public void apply_to_should_assign_empty_conventions_to_api_version_neutral_cont
var controllerDescriptor = mock.Object;
var controllerBuilder = default( IControllerConventionBuilder );
- mock.Setup( cd => cd.GetCustomAttributes() ).Returns( new Collection() );
+ mock.Setup( cd => cd.GetCustomAttributes() ).Returns( [] );
controllerDescriptor.Configuration = configuration;
controllerDescriptor.ControllerType = typeof( UndecoratedController );
configuration.AddApiVersioning( o => controllerBuilder = o.Conventions.Controller() );
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs
index 6990e951..eedc943c 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs
@@ -20,7 +20,7 @@ public void apply_to_should_assign_conventions_to_controller()
var controllerDescriptor = mock.Object;
var controllerBuilder = default( IControllerConventionBuilder );
- mock.Setup( cd => cd.GetCustomAttributes() ).Returns( new Collection() );
+ mock.Setup( cd => cd.GetCustomAttributes() ).Returns( [] );
controllerDescriptor.Configuration = configuration;
controllerDescriptor.ControllerType = typeof( UndecoratedController );
configuration.AddApiVersioning( o => controllerBuilder = o.Conventions.Controller( typeof( UndecoratedController ) ) );
@@ -55,7 +55,7 @@ public void apply_to_should_assign_empty_conventions_to_api_version_neutral_cont
var controllerDescriptor = mock.Object;
var controllerBuilder = default( IControllerConventionBuilder );
- mock.Setup( cd => cd.GetCustomAttributes() ).Returns( new Collection() );
+ mock.Setup( cd => cd.GetCustomAttributes() ).Returns( [] );
controllerDescriptor.Configuration = configuration;
controllerDescriptor.ControllerType = typeof( UndecoratedController );
configuration.AddApiVersioning( o => controllerBuilder = o.Conventions.Controller( typeof( UndecoratedController ) ) );
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/DefaultApiVersionReporterTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/DefaultApiVersionReporterTest.cs
index dc3f2eb5..23e70e9e 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/DefaultApiVersionReporterTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/DefaultApiVersionReporterTest.cs
@@ -5,7 +5,6 @@ namespace Asp.Versioning;
using Asp.Versioning.Simulators;
using System.Web.Http;
using System.Web.Http.Controllers;
-using System.Web.Http.Dependencies;
using static System.Net.HttpStatusCode;
public class DefaultApiVersionReporterTest
@@ -14,9 +13,8 @@ public class DefaultApiVersionReporterTest
public void report_should_add_expected_headers()
{
// arrange
- var reporter = new DefaultApiVersionReporter();
var sunsetDate = DateTimeOffset.Now;
- var dependencyResolver = new Mock();
+ var reporter = new DefaultApiVersionReporter( new TestSunsetPolicyManager( sunsetDate ) );
var configuration = new HttpConfiguration();
var request = new HttpRequestMessage();
var response = new HttpResponseMessage( OK ) { RequestMessage = request };
@@ -34,9 +32,6 @@ public void report_should_add_expected_headers()
deprecatedAdvertisedVersions: Enumerable.Empty() );
var metadata = new ApiVersionMetadata( apiModel, endpointModel, "Test" );
- dependencyResolver.Setup( dr => dr.GetService( typeof( ISunsetPolicyManager ) ) )
- .Returns( new TestSunsetPolicyManager( sunsetDate ) );
- configuration.DependencyResolver = dependencyResolver.Object;
request.SetConfiguration( configuration );
request.ApiVersionProperties().RequestedApiVersion = new ApiVersion( 1.0 );
request.Properties["MS_HttpActionDescriptor"] =
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.AmbiguousControllers.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.AmbiguousControllers.cs
index 80957cb1..15512777 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.AmbiguousControllers.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.AmbiguousControllers.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1812
-#pragma warning disable CA1822 // Mark members as static
#pragma warning disable SA1601 // Partial elements should be documented
namespace Asp.Versioning.Dispatcher;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.cs
index 3dc5df0d..e4c523fb 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.cs
@@ -1,5 +1,8 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+//// Ignore Spelling: Dbased
+//// Ignore Spelling: Dneutral
+
namespace Asp.Versioning.Dispatcher;
using Asp.Versioning;
@@ -163,7 +166,7 @@ public void select_controller_should_return_correct_versionX2DneutralX2C_convent
}
[Fact]
- public async Task select_controller_should_return_404_for_unmatchedX2C_attributeX2Dbased_controller_version()
+ public async Task select_controller_should_return_400_for_unmatchedX2C_attributeX2Dbased_controller_version()
{
// arrange
var detail = "The HTTP resource that matches the request URI 'http://localhost/api/test' does not support the API version '42.0'.";
@@ -190,13 +193,13 @@ public async Task select_controller_should_return_404_for_unmatchedX2C_attribute
var content = await response.Content.ReadAsProblemDetailsAsync();
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "1.0, 2.0, 3.0, 4.0" );
response.Headers.GetValues( "api-deprecated-versions" ).Single().Should().Be( "3.0-Alpha" );
content.Should().BeEquivalentTo(
new ProblemDetails()
{
- Status = 404,
+ Status = 400,
Title = "Unsupported API version",
Type = ProblemDetailsDefaults.Unsupported.Type,
Detail = detail,
@@ -255,7 +258,7 @@ public async Task select_controller_should_return_400_for_attributeX2Dbased_cont
}
[Fact]
- public async Task select_controller_should_return_404_for_unmatchedX2C_conventionX2Dbased_controller_version()
+ public async Task select_controller_should_return_400_for_unmatchedX2C_conventionX2Dbased_controller_version()
{
// arrange
var detail = "The HTTP resource that matches the request URI 'http://localhost/api/test' does not support the API version '4.0'.";
@@ -283,13 +286,13 @@ public async Task select_controller_should_return_404_for_unmatchedX2C_conventio
var content = await response.Content.ReadAsProblemDetailsAsync();
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "1.0, 2.0, 3.0" );
response.Headers.GetValues( "api-deprecated-versions" ).Single().Should().Be( "1.8, 1.9" );
content.Should().BeEquivalentTo(
new ProblemDetails()
{
- Status = 404,
+ Status = 400,
Title = "Unsupported API version",
Type = ProblemDetailsDefaults.Unsupported.Type,
Detail = detail,
@@ -413,7 +416,7 @@ public void select_controller_should_return_400_when_no_version_is_specified_and
}
[Fact]
- public void select_controller_should_return_404_for_unmatched_action()
+ public void select_controller_should_return_400_for_unmatched_action()
{
// arrange
var configuration = AttributeRoutingEnabledConfiguration;
@@ -433,7 +436,7 @@ public void select_controller_should_return_404_for_unmatched_action()
var response = selectController.Should().Throw().Subject.Single().Response;
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "1.0, 2.0, 3.0, 4.0" );
response.Headers.GetValues( "api-deprecated-versions" ).Single().Should().Be( "3.0-Alpha" );
}
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderBuilderTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderBuilderTest.cs
index b980fc31..499d07fe 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderBuilderTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderBuilderTest.cs
@@ -125,7 +125,7 @@ public void read_should_retrieve_version_from_content_type_and_accept()
var versions = reader.Read( request );
// assert
- versions.Should().BeEquivalentTo( new[] { "1.5", "2.0" } );
+ versions.Should().BeEquivalentTo( ["1.5", "2.0"] );
}
[Fact]
@@ -246,7 +246,7 @@ public void read_should_only_retrieve_included_media_types()
[InlineData( "application/vnd-v{v}+json", "v", "application/vnd-v2.1+json", "2.1" )]
[InlineData( "application/vnd-v{ver}+json", "ver", "application/vnd-v2022-11-01+json", "2022-11-01" )]
[InlineData( "application/vnd-{version}+xml", "version", "application/vnd-1.1-beta+xml", "1.1-beta" )]
- public void read_should_retreive_version_from_media_type_template(
+ public void read_should_retrieve_version_from_media_type_template(
string template,
string parameterName,
string mediaType,
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderTest.cs
index bc4487b9..ea9ee138 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderTest.cs
@@ -111,7 +111,7 @@ public void read_should_retrieve_version_from_content_type_and_accept()
var versions = reader.Read( request );
// assert
- versions.Should().BeEquivalentTo( new[] { "1.5", "2.0" } );
+ versions.Should().BeEquivalentTo( ["1.5", "2.0"] );
}
[Fact]
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/QueryStringApiVersionReaderTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/QueryStringApiVersionReaderTest.cs
index 6355badf..c03525f9 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/QueryStringApiVersionReaderTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/QueryStringApiVersionReaderTest.cs
@@ -86,8 +86,8 @@ public void read_should_not_throw_exception_when_duplicate_api_versions_are_requ
}
[Theory]
- [InlineData( new object[] { new string[0] } )]
- [InlineData( new object[] { new[] { "api-version" } } )]
+ [InlineData( [new string[0]] )]
+ [InlineData( [new[] { "api-version" }] )]
public void add_parameters_should_add_single_parameter_from_query_string( string[] parameterNames )
{
// arrange
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/ReportApiVersionsAttributeTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/ReportApiVersionsAttributeTest.cs
index 57090ed2..ec30300a 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/ReportApiVersionsAttributeTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/ReportApiVersionsAttributeTest.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+//// Ignore Spelling: Dneutral
+
namespace Asp.Versioning;
using Asp.Versioning.Simulators;
@@ -29,11 +31,12 @@ public void on_action_executed_should_add_version_headers()
var method = controller.GetType().GetMethod( nameof( TestController.Get ) );
var controllerDescriptor = new Mock( configuration, "Test", controller.GetType() ) { CallBase = true };
var routeData = new HttpRouteData( new HttpRoute( "api/tests" ) );
- var controllerContext = new HttpControllerContext( new HttpConfiguration(), routeData, new HttpRequestMessage() ) { Controller = controller };
+ var controllerContext = new HttpControllerContext( configuration, routeData, new HttpRequestMessage() ) { Controller = controller };
var actionDescriptor = new ReflectedHttpActionDescriptor( controllerDescriptor.Object, method );
var actionContext = new HttpActionContext( controllerContext, actionDescriptor ) { Response = new HttpResponseMessage() };
var context = new HttpActionExecutedContext( actionContext, null );
+ configuration.AddApiVersioning( options => options.ReportApiVersions = true );
controllerContext.Request.SetConfiguration( new() );
controllerContext.Request.Properties["MS_HttpActionDescriptor"] = actionDescriptor;
controllerDescriptor.Setup( cd => cd.GetCustomAttributes( It.IsAny() ) ).Returns( attributes );
@@ -69,11 +72,12 @@ public void on_action_executing_should_not_add_headers_for_versionX2Dneutral_con
var method = controller.GetType().GetMethod( nameof( TestVersionNeutralController.Get ) );
var controllerDescriptor = new Mock( configuration, "Test", controller.GetType() ) { CallBase = true };
var routeData = new HttpRouteData( new HttpRoute( "api/tests" ) );
- var controllerContext = new HttpControllerContext( new HttpConfiguration(), routeData, new HttpRequestMessage() ) { Controller = new TestVersionNeutralController() };
+ var controllerContext = new HttpControllerContext( configuration, routeData, new HttpRequestMessage() ) { Controller = new TestVersionNeutralController() };
var actionDescriptor = new ReflectedHttpActionDescriptor( controllerDescriptor.Object, method );
var actionContext = new HttpActionContext( controllerContext, actionDescriptor ) { Response = new HttpResponseMessage() };
var context = new HttpActionExecutedContext( actionContext, null );
+ configuration.AddApiVersioning();
controllerDescriptor.Setup( cd => cd.GetCustomAttributes( It.IsAny() ) ).Returns( attributes );
controllerDescriptor.Object.Properties[typeof( ApiVersionModel )] = ApiVersionModel.Neutral;
actionDescriptor.Properties[typeof( ApiVersionMetadata )] = ApiVersionMetadata.Neutral;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AdminController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AdminController.cs
index ae3bbbd2..6bcd08cc 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AdminController.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AdminController.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+//// Ignore Spelling: Admin
+
namespace Asp.Versioning.Simulators;
using System.Web.Http;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRoute2Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRoute2Controller.cs
index 51c53117..34fc582c 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRoute2Controller.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRoute2Controller.cs
@@ -1,6 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRouteController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRouteController.cs
index 39a16df1..1cc6df30 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRouteController.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRouteController.cs
@@ -1,6 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest2Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest2Controller.cs
index c0af43b2..257bbc60 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest2Controller.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest2Controller.cs
@@ -1,6 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest4Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest4Controller.cs
index 84b21dae..f31b3581 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest4Controller.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest4Controller.cs
@@ -1,6 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTestController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTestController.cs
index 11374a40..bf3e867c 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTestController.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTestController.cs
@@ -1,6 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1822 // Mark members as static
+#pragma warning disable IDE0060
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ConventionsController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ConventionsController.cs
index 5053fc3d..c7cd7e91 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ConventionsController.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ConventionsController.cs
@@ -1,6 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/NeutralController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/NeutralController.cs
index 18d1a70a..8101ce4f 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/NeutralController.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/NeutralController.cs
@@ -1,6 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/OrdersController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/OrdersController.cs
index cc482c4a..1d42a805 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/OrdersController.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/OrdersController.cs
@@ -1,6 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1707 // Identifiers should not contain underscores
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestController.cs
index a141a1e6..3c145fb1 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestController.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestController.cs
@@ -1,6 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersion2Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersion2Controller.cs
index c6c87bdf..89d77a80 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersion2Controller.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersion2Controller.cs
@@ -1,6 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersionNeutralController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersionNeutralController.cs
index 89369fe3..d9ceb578 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersionNeutralController.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersionNeutralController.cs
@@ -1,6 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.Simulators;
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs
index a77f8010..22cec40e 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs
@@ -37,4 +37,17 @@ public void add_api_versioning_should_report_api_versions_when_option_is_enabled
configuration.Services.GetActionSelector().Should().BeOfType();
configuration.Filters.Single().Instance.Should().BeOfType();
}
+
+ [Fact]
+ public void add_api_versioning_should_not_allow_default_neutral_api_version()
+ {
+ // arrange
+ var configuration = new HttpConfiguration();
+
+ // act
+ Action options = () => configuration.AddApiVersioning( options => options.DefaultApiVersion = ApiVersion.Neutral );
+
+ // assert
+ options.Should().Throw();
+ }
}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpRouteCollectionExtensionsTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpRouteCollectionExtensionsTest.cs
index 8d270992..eb271b38 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpRouteCollectionExtensionsTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpRouteCollectionExtensionsTest.cs
@@ -56,7 +56,7 @@ namespace System.Web.Http.WebHost.Routing
internal sealed class HostedHttpRouteCollection : HttpRouteCollection
{
#pragma warning disable SA1309 // Field names should not begin with underscore
- private readonly RouteCollection _routeCollection = new();
+ private readonly RouteCollection _routeCollection = [];
#pragma warning restore SA1309 // Field names should not begin with underscore
public override string VirtualPathRoot => throw NotUsedInUnitTest();
@@ -131,7 +131,7 @@ namespace System.Web.Http.WebHost.Routing
internal sealed class HttpWebRoute : Route
{
public HttpWebRoute( IHttpRoute httpRoute )
- : base( httpRoute.RouteTemplate, new(), new(), new(), Mock.Of() ) => HttpRoute = httpRoute;
+ : base( httpRoute.RouteTemplate, [], [], [], Mock.Of() ) => HttpRoute = httpRoute;
public IHttpRoute HttpRoute { get; }
}
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Asp.Versioning.Mvc.Acceptance.Tests.csproj b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Asp.Versioning.Mvc.Acceptance.Tests.csproj
index 4a88a59d..d891b0a2 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Asp.Versioning.Mvc.Acceptance.Tests.csproj
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Asp.Versioning.Mvc.Acceptance.Tests.csproj
@@ -1,13 +1,13 @@
- net7.0
+ $(DefaultTargetFramework)
Asp.Versioning
-
-
+
+
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/FilteredControllerTypes.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/FilteredControllerTypes.cs
index 6509a017..f84e913c 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/FilteredControllerTypes.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/FilteredControllerTypes.cs
@@ -8,7 +8,7 @@ namespace Asp.Versioning;
internal sealed class FilteredControllerTypes : ControllerFeatureProvider, ICollection
{
- private readonly HashSet controllerTypes = new();
+ private readonly HashSet controllerTypes = [];
protected override bool IsController( TypeInfo typeInfo ) => base.IsController( typeInfo ) && controllerTypes.Contains( typeInfo );
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MediaTypeFixture.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MediaTypeFixture.cs
new file mode 100644
index 00000000..5736c1bf
--- /dev/null
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MediaTypeFixture.cs
@@ -0,0 +1,9 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.Http;
+
+public class MediaTypeFixture : MinimalApiFixture
+{
+ protected override void OnAddApiVersioning( ApiVersioningOptions options ) =>
+ options.ApiVersionReader = new MediaTypeApiVersionReader();
+}
\ No newline at end of file
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs
index 208c5130..20789427 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs
@@ -1,6 +1,9 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-namespace Asp.Versioning;
+#pragma warning disable IDE0079 // Remove unnecessary suppression
+#pragma warning disable ASP0018 // Unused route parameter
+
+namespace Asp.Versioning.Http;
using Asp.Versioning.Conventions;
using Microsoft.AspNetCore.Builder;
@@ -11,24 +14,16 @@ public class MinimalApiFixture : HttpServerFixture
{
protected override void OnConfigureEndpoints( IEndpointRouteBuilder endpoints )
{
+ endpoints.MapGet( "api/ping", () => Results.NoContent() )
+ .WithApiVersionSet( endpoints.NewApiVersionSet().Build() )
+ .IsApiVersionNeutral();
+
var values = endpoints.NewApiVersionSet( "Values" )
.HasApiVersion( 1.0 )
.HasApiVersion( 2.0 )
.ReportApiVersions()
.Build();
- var helloWorld = endpoints.NewApiVersionSet( "Hello World" )
- .HasApiVersion( 1.0 )
- .HasApiVersion( 2.0 )
- .ReportApiVersions()
- .Build();
-
- var orders = endpoints.NewApiVersionSet( "Orders" ).Build();
-
- endpoints.MapGet( "api/ping", () => Results.NoContent() )
- .WithApiVersionSet( endpoints.NewApiVersionSet().Build() )
- .IsApiVersionNeutral();
-
endpoints.MapGet( "api/values", () => "Value 1" )
.WithApiVersionSet( values )
.MapToApiVersion( 1.0 );
@@ -37,43 +32,34 @@ protected override void OnConfigureEndpoints( IEndpointRouteBuilder endpoints )
.WithApiVersionSet( values )
.MapToApiVersion( 2.0 );
- endpoints.MapGet( "api/v{version:apiVersion}/hello", () => "Hello world!" )
- .WithApiVersionSet( helloWorld )
- .MapToApiVersion( 1.0 );
-
- endpoints.MapGet( "api/v{version:apiVersion}/hello/{text}", ( string text ) => text )
- .WithApiVersionSet( helloWorld )
- .MapToApiVersion( 1.0 );
-
- endpoints.MapGet( "api/v{version:apiVersion}/hello", () => "Hello world! (v2)" )
- .WithApiVersionSet( helloWorld )
- .MapToApiVersion( 2.0 );
-
- endpoints.MapGet( "api/v{version:apiVersion}/hello/{text}", ( string text ) => text + " (v2)" )
- .WithApiVersionSet( helloWorld )
- .MapToApiVersion( 2.0 );
-
- endpoints.MapPost( "api/v{version:apiVersion}/hello", () => { } )
- .WithApiVersionSet( helloWorld );
+ var orders = endpoints.NewVersionedApi( "Orders" )
+ .MapGroup( "api/order" )
+ .HasApiVersion( 1.0 )
+ .HasApiVersion( 2.0 );
- endpoints.MapGet( "api/order", () => { } )
- .WithApiVersionSet( orders )
- .HasApiVersion( 1.0 )
- .HasApiVersion( 2.0 );
+ orders.MapGet( "/", () => Results.Ok() );
+ orders.MapGet( "/{id}", ( int id ) => Results.Ok() ).HasDeprecatedApiVersion( 0.9 );
+ orders.MapPost( "/", () => Results.Created() );
+ orders.MapDelete( "/{id}", ( int id ) => Results.NoContent() ).IsApiVersionNeutral();
- endpoints.MapGet( "api/order/{id}", ( int id ) => { } )
- .WithApiVersionSet( orders )
- .HasDeprecatedApiVersion( 0.9 )
- .HasApiVersion( 1.0 )
- .HasApiVersion( 2.0 );
+ var helloWorld = endpoints.NewVersionedApi( "Orders" )
+ .MapGroup( "api/v{version:apiVersion}/hello" )
+ .HasApiVersion( 1.0 )
+ .HasApiVersion( 2.0 )
+ .ReportApiVersions();
- endpoints.MapPost( "api/order", () => { } )
- .WithApiVersionSet( orders )
- .HasApiVersion( 1.0 )
- .HasApiVersion( 2.0 );
+ helloWorld.MapGet( "/", () => "Hello world!" ).MapToApiVersion( 1.0 );
+ helloWorld.MapGet( "/{text}", ( string text ) => text ).MapToApiVersion( 1.0 );
+ helloWorld.MapGet( "/", () => "Hello world! (v2)" ).MapToApiVersion( 2.0 );
+ helloWorld.MapGet( "/{text}", ( string text ) => text + " (v2)" ).MapToApiVersion( 2.0 );
+ helloWorld.MapPost( "/", () => { } );
+ }
- endpoints.MapDelete( "api/order/{id}", ( int id ) => { } )
- .WithApiVersionSet( orders )
- .IsApiVersionNeutral();
+ protected override void OnAddApiVersioning( ApiVersioningOptions options )
+ {
+ options.ApiVersionReader = ApiVersionReader.Combine(
+ new QueryStringApiVersionReader(),
+ new UrlSegmentApiVersionReader(),
+ new MediaTypeApiVersionReader() );
}
}
\ No newline at end of file
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/given a versioned minimal API/when using a media type.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/given a versioned minimal API/when using a media type.cs
new file mode 100644
index 00000000..ee292d3d
--- /dev/null
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/given a versioned minimal API/when using a media type.cs
@@ -0,0 +1,35 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.Http;
+
+using Asp.Versioning;
+using System.Net.Http;
+using System.Net.Http.Json;
+using static System.Net.Http.Headers.MediaTypeWithQualityHeaderValue;
+using static System.Net.Http.HttpMethod;
+using static System.Net.HttpStatusCode;
+
+public class when_using_a_media_type : AcceptanceTest, IClassFixture
+{
+ [Fact]
+ public async Task problem_details_should_be_returned_for_accept_header_with_unsupported_api_version()
+ {
+ // arrange
+ using var request = new HttpRequestMessage( Post, "api/values" )
+ {
+ Headers = { Accept = { Parse( "application/json;v=3.0" ) } },
+ Content = JsonContent.Create( new { test = true }, Parse( "application/json;v=3.0" ) ),
+ };
+
+ // act
+ var response = await Client.SendAsync( request );
+ var problem = await response.Content.ReadAsProblemDetailsAsync();
+
+ // assert
+ response.StatusCode.Should().Be( UnsupportedMediaType );
+ problem.Type.Should().Be( ProblemDetailsDefaults.Unsupported.Type );
+ }
+
+ public when_using_a_media_type( MediaTypeFixture fixture, ITestOutputHelper console )
+ : base( fixture ) => console.WriteLine( fixture.DirectedGraphVisualizationUrl );
+}
\ No newline at end of file
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/given a versioned minimal API/when using an endpoint.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/given a versioned minimal API/when using an endpoint.cs
index 45329292..de479673 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/given a versioned minimal API/when using an endpoint.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/given a versioned minimal API/when using an endpoint.cs
@@ -11,7 +11,7 @@ namespace given_a_versioned_minimal_API;
public class when_using_an_endpoint : AcceptanceTest
{
[Theory]
- [InlineData( "api/order?api-version=0.9", NotFound )]
+ [InlineData( "api/order?api-version=0.9", BadRequest )]
[InlineData( "api/order?api-version=1.0", OK )]
[InlineData( "api/order?api-version=2.0", OK )]
[InlineData( "api/order/42?api-version=0.9", OK )]
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/HttpServerFixture.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/HttpServerFixture.cs
index e04031d1..57c872e1 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/HttpServerFixture.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/HttpServerFixture.cs
@@ -1,5 +1,8 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+// Ignore Spelling: app
+// Ignore Spelling: Mvc
namespace Asp.Versioning;
using Microsoft.AspNetCore.Builder;
@@ -18,7 +21,7 @@ public abstract partial class HttpServerFixture
{
private string visualizationUrl;
- public string DirectedGraphVisualizationUrl =>
+ internal string DirectedGraphVisualizationUrl =>
visualizationUrl ??= GenerateEndpointDirectedGraph( Server.Services );
protected virtual void OnConfigurePartManager( ApplicationPartManager partManager ) =>
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OrdersController.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OrdersController.cs
index 2d94fbaa..5fc09eb7 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OrdersController.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OrdersController.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+#pragma warning disable IDE0060
+
namespace Asp.Versioning.Mvc.UsingAttributes.Controllers;
using Asp.Versioning.Mvc.UsingAttributes.Models;
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OverlappingRouteTemplateController.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OverlappingRouteTemplateController.cs
index da532780..f028c66f 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OverlappingRouteTemplateController.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OverlappingRouteTemplateController.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1822 // Mark members as static
+#pragma warning disable CA1822
+#pragma warning disable IDE0060
namespace Asp.Versioning.Mvc.UsingAttributes.Controllers;
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/given a versioned Controller/when using a query string and split into two types.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/given a versioned Controller/when using a query string and split into two types.cs
index f692f92d..7c78efd4 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/given a versioned Controller/when using a query string and split into two types.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/given a versioned Controller/when using a query string and split into two types.cs
@@ -87,7 +87,7 @@ public async Task then_delete_should_return_405( string apiVersion )
}
[Fact]
- public async Task then_get_should_return_404_for_an_unsupported_version()
+ public async Task then_get_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -96,7 +96,7 @@ public async Task then_get_should_return_404_for_an_unsupported_version()
var response = await GetAsync( "api/values?api-version=3.0" );
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
}
[Fact]
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingConventions/Controllers/OrdersController.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingConventions/Controllers/OrdersController.cs
index 8d1d1a34..8c481dc2 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingConventions/Controllers/OrdersController.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingConventions/Controllers/OrdersController.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+#pragma warning disable IDE0060 // Remove unused parameter
+
namespace Asp.Versioning.Mvc.UsingConventions.Controllers;
using Asp.Versioning.Mvc.UsingConventions.Models;
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingConventions/given a versioned Controller using conventions/when using a query string and split into two types.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingConventions/given a versioned Controller using conventions/when using a query string and split into two types.cs
index 069d4c36..081fae3c 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingConventions/given a versioned Controller using conventions/when using a query string and split into two types.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingConventions/given a versioned Controller using conventions/when using a query string and split into two types.cs
@@ -29,7 +29,7 @@ public async Task then_get_should_return_200( string controller, string apiVersi
}
[Fact]
- public async Task then_get_should_return_404_for_an_unsupported_version()
+ public async Task then_get_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -38,7 +38,7 @@ public async Task then_get_should_return_404_for_an_unsupported_version()
var response = await GetAsync( "api/values?api-version=4.0" );
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
}
[Fact]
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V1/OrdersController.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V1/OrdersController.cs
index 1006619f..8b935d1e 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V1/OrdersController.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V1/OrdersController.cs
@@ -1,6 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1716 // Identifiers should not match keywords
namespace Asp.Versioning.Mvc.UsingNamespace.Controllers.V1;
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V2/OrdersController.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V2/OrdersController.cs
index 0d7e007a..9804051b 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V2/OrdersController.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V2/OrdersController.cs
@@ -1,6 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1716 // Identifiers should not match keywords
namespace Asp.Versioning.Mvc.UsingNamespace.Controllers.V2;
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/given a versioned Controller per namespace/when using a query string.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/given a versioned Controller per namespace/when using a query string.cs
index 18669a23..7a1f29a4 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/given a versioned Controller per namespace/when using a query string.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/given a versioned Controller per namespace/when using a query string.cs
@@ -34,7 +34,7 @@ public async Task then_get_should_return_200( Type controllerType, string apiVer
}
[Fact]
- public async Task then_get_should_return_404_for_an_unsupported_version()
+ public async Task then_get_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -43,7 +43,7 @@ public async Task then_get_should_return_404_for_an_unsupported_version()
var response = await GetAsync( "api/agreements/42?api-version=4.0" );
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
}
[Fact]
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Advanced/given a versioned ODataController mixed with base controllers/when people is any version.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Advanced/given a versioned ODataController mixed with base controllers/when people is any version.cs
index dc1ff8bb..82aced7b 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Advanced/given a versioned ODataController mixed with base controllers/when people is any version.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Advanced/given a versioned ODataController mixed with base controllers/when people is any version.cs
@@ -2,14 +2,13 @@
namespace given_a_versioned_ODataController_mixed_with_base_controllers;
-using Asp.Versioning;
using Asp.Versioning.OData.Advanced;
using static System.Net.HttpStatusCode;
public class when_people_is_any_version : AdvancedAcceptanceTest
{
[Fact]
- public async Task then_patch_should_return_404_for_an_unsupported_version()
+ public async Task then_patch_should_return_400_for_an_unsupported_version()
{
// arrange
var person = new { lastName = "Me" };
@@ -18,7 +17,7 @@ public async Task then_patch_should_return_404_for_an_unsupported_version()
var response = await PatchAsync( $"api/people/42?api-version=4.0", person );
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
}
[Fact]
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs
index e138f931..0294fe55 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
#pragma warning disable IDE0060 // Remove unused parameter
+#pragma warning disable IDE0079
#pragma warning disable CA1822 // Mark members as static
namespace Asp.Versioning.OData.Basic.Controllers;
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string and split into two types.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string and split into two types.cs
index 1cdad01e..e8c60654 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string and split into two types.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string and split into two types.cs
@@ -28,7 +28,7 @@ public async Task then_get_should_return_200( string requestUrl )
}
[Fact]
- public async Task then_get_should_return_404_for_an_unsupported_version()
+ public async Task then_get_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -37,7 +37,7 @@ public async Task then_get_should_return_404_for_an_unsupported_version()
var response = await GetAsync( "api/people?api-version=4.0" );
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
}
[Fact]
@@ -84,7 +84,7 @@ public async Task then_delete_should_return_405_for_unmatched_action()
}
[Fact]
- public async Task then_patch_should_return_404_for_an_unsupported_version()
+ public async Task then_patch_should_return_400_for_an_unsupported_version()
{
// arrange
var person = new { id = 42, firstName = "John", lastName = "Doe", email = "john.doe@somewhere.com" };
@@ -93,7 +93,7 @@ public async Task then_patch_should_return_404_for_an_unsupported_version()
var response = await PatchAsync( "api/people/42?api-version=4.0", person );
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
}
[Fact]
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string.cs
index 4e8cce19..7cea36c0 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given a versioned ODataController/when using a query string.cs
@@ -17,14 +17,14 @@ public async Task then_get_should_return_200( string requestUrl )
// act
- var response = (await GetAsync( requestUrl )).EnsureSuccessStatusCode();
+ var response = ( await GetAsync( requestUrl ) ).EnsureSuccessStatusCode();
// assert
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "1.0" );
}
[Fact]
- public async Task then_get_should_return_404_for_an_unsupported_version()
+ public async Task then_get_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -33,7 +33,7 @@ public async Task then_get_should_return_404_for_an_unsupported_version()
var response = await GetAsync( "api/orders?api-version=2.0" );
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
}
[Fact]
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a url segment.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a url segment.cs
index cb9cc0fa..8d873512 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a url segment.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a url segment.cs
@@ -1,5 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+#pragma warning disable IDE0079
#pragma warning disable CA2000 // Dispose objects before losing scope
namespace given_versioned_batch_middleware;
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs
index e9f86835..b6ba5605 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-
namespace Asp.Versioning.OData.Configuration;
using Asp.Versioning.OData.Models;
@@ -27,6 +25,9 @@ private static EntityTypeConfiguration ConfigureCurrent( ODataModelBui
public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix )
{
+ ArgumentNullException.ThrowIfNull( builder );
+ ArgumentNullException.ThrowIfNull( apiVersion );
+
switch ( apiVersion.MajorVersion )
{
case 1:
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs
index 6cb4a514..20cbfd11 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-
namespace Asp.Versioning.OData.Configuration;
using Asp.Versioning.OData.Models;
@@ -24,6 +22,8 @@ private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuilde
public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix )
{
+ ArgumentNullException.ThrowIfNull( builder );
+
if ( supportedApiVersion == null || supportedApiVersion == apiVersion )
{
ConfigureCurrent( builder );
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs
index 46e67287..0c49a498 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-
namespace Asp.Versioning.OData.Configuration;
using Asp.Versioning.OData.Models;
@@ -27,6 +25,9 @@ private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuild
public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix )
{
+ ArgumentNullException.ThrowIfNull( builder );
+ ArgumentNullException.ThrowIfNull( apiVersion );
+
switch ( apiVersion.MajorVersion )
{
case 1:
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs
index 62a23acc..4456fa39 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-
namespace Asp.Versioning.OData.Configuration;
using Asp.Versioning.OData.Models;
@@ -24,6 +22,8 @@ private static EntityTypeConfiguration ConfigureCurrent( ODataM
public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix )
{
+ ArgumentNullException.ThrowIfNull( builder );
+
if ( supportedApiVersion == null || supportedApiVersion == apiVersion )
{
ConfigureCurrent( builder );
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/ODataAcceptanceTest.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/ODataAcceptanceTest.cs
index 3bec69f6..4287b9a1 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/ODataAcceptanceTest.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/ODataAcceptanceTest.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+//// Ignore Spelling: Dspecific
+
namespace Asp.Versioning.OData;
using static System.Net.HttpStatusCode;
@@ -37,7 +39,7 @@ public async Task then_the_service_document_should_be_versionX2Dspecific( string
}
[Fact]
- public async Task then_the_service_document_should_return_404_for_an_unsupported_version()
+ public async Task then_the_service_document_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -46,7 +48,7 @@ public async Task then_the_service_document_should_return_404_for_an_unsupported
var response = await GetAsync( "api?api-version=4.0" );
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
}
[Fact]
@@ -79,7 +81,7 @@ public async Task then_X24metadata_should_be_versionX2Dspecific( string apiVersi
}
[Fact]
- public async Task then_X24metadata_should_return_404_for_an_unsupported_version()
+ public async Task then_X24metadata_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -88,7 +90,7 @@ public async Task then_X24metadata_should_return_404_for_an_unsupported_version(
var response = await GetAsync( "api/$metadata?api-version=4.0" );
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
}
protected ODataAcceptanceTest( ODataFixture fixture ) : base( fixture ) { }
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string and split into two types.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string and split into two types.cs
index 91d65526..cf15f390 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string and split into two types.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string and split into two types.cs
@@ -28,7 +28,7 @@ public async Task then_get_should_return_200( string requestUrl )
}
[Fact]
- public async Task then_get_should_return_404_for_an_unsupported_version()
+ public async Task then_get_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -37,7 +37,7 @@ public async Task then_get_should_return_404_for_an_unsupported_version()
var response = await GetAsync( "api/people?api-version=4.0" );
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
}
[Fact]
@@ -71,7 +71,7 @@ public async Task then_patch_should_return_400_if_supported_in_any_version( stri
}
[Fact]
- public async Task then_patch_should_return_404_for_an_unsupported_version()
+ public async Task then_patch_should_return_400_for_an_unsupported_version()
{
// arrange
var person = new { id = 42, firstName = "John", lastName = "Doe", email = "john.doe@somewhere.com" };
@@ -80,7 +80,7 @@ public async Task then_patch_should_return_404_for_an_unsupported_version()
var response = await PatchAsync( "api/people/42?api-version=4.0", person );
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
}
[Fact]
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string.cs
index d054f0b1..9ae9f4c0 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/UsingConventions/given a versioned ODataController using conventions/when using a query string.cs
@@ -24,7 +24,7 @@ public async Task then_get_should_return_200( string requestUrl )
}
[Fact]
- public async Task then_get_should_return_404_for_an_unsupported_version()
+ public async Task then_get_should_return_400_for_an_unsupported_version()
{
// arrange
@@ -33,7 +33,7 @@ public async Task then_get_should_return_404_for_an_unsupported_version()
var response = await GetAsync( "api/orders?api-version=2.0" );
// assert
- response.StatusCode.Should().Be( NotFound );
+ response.StatusCode.Should().Be( BadRequest );
}
[Fact]
diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/TestApplicationPart.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/TestApplicationPart.cs
index e107ef34..03c8f591 100644
--- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/TestApplicationPart.cs
+++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/TestApplicationPart.cs
@@ -1,27 +1,26 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-namespace Asp.Versioning
-{
- using Microsoft.AspNetCore.Mvc.ApplicationParts;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
+namespace Asp.Versioning;
+
+using Microsoft.AspNetCore.Mvc.ApplicationParts;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
- internal sealed class TestApplicationPart : ApplicationPart, IApplicationPartTypeProvider
- {
- public TestApplicationPart() => Types = Enumerable.Empty();
+internal sealed class TestApplicationPart : ApplicationPart, IApplicationPartTypeProvider
+{
+ public TestApplicationPart() => Types = Enumerable.Empty();
- public TestApplicationPart( params TypeInfo[] types ) => Types = types;
+ public TestApplicationPart( params TypeInfo[] types ) => Types = types;
- public TestApplicationPart( IEnumerable types ) => Types = types;
+ public TestApplicationPart( IEnumerable types ) => Types = types;
- public TestApplicationPart( IEnumerable types ) : this( types.Select( t => t.GetTypeInfo() ) ) { }
+ public TestApplicationPart( IEnumerable types ) : this( types.Select( t => t.GetTypeInfo() ) ) { }
- public TestApplicationPart( params Type[] types ) : this( types.Select( t => t.GetTypeInfo() ) ) { }
+ public TestApplicationPart( params Type[] types ) : this( types.Select( t => t.GetTypeInfo() ) ) { }
- public override string Name => "Test Part";
+ public override string Name => "Test Part";
- public IEnumerable Types { get; }
- }
+ public IEnumerable Types { get; }
}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ApiDescriptionExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ApiDescriptionExtensions.cs
new file mode 100644
index 00000000..29520295
--- /dev/null
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ApiDescriptionExtensions.cs
@@ -0,0 +1,21 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Microsoft.AspNetCore.Mvc.ApiExplorer;
+
+internal static class ApiDescriptionExtensions
+{
+ internal static bool IsODataLike( this ApiDescription description )
+ {
+ var parameters = description.ActionDescriptor.Parameters;
+
+ for ( var i = 0; i < parameters.Count; i++ )
+ {
+ if ( parameters[i].ParameterType.IsODataQueryOptions() )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs
index 0084cd01..fdb4ff8d 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs
@@ -92,10 +92,7 @@ public ODataApiDescriptionProvider(
/// The default implementation performs no action.
public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context )
{
- if ( context == null )
- {
- throw new ArgumentNullException( nameof( context ) );
- }
+ ArgumentNullException.ThrowIfNull( context );
var results = context.Results;
var visited = new HashSet( capacity: results.Count, new ApiDescriptionComparer() );
@@ -145,6 +142,7 @@ public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context )
else
{
UpdateModelTypes( result, matched );
+ UpdateFunctionCollectionParameters( result, matched );
}
}
@@ -201,7 +199,7 @@ private static bool IsNavigationPropertyLink( ODataPathTemplate template ) =>
private static bool TryMatchModelVersion(
ApiDescription description,
- IReadOnlyList items,
+ IODataRoutingMetadata[] items,
[NotNullWhen( true )] out IODataRoutingMetadata? metadata )
{
if ( description.GetApiVersion() is not ApiVersion apiVersion )
@@ -218,10 +216,10 @@ private static bool TryMatchModelVersion(
return false;
}
- for ( var i = 0; i < items.Count; i++ )
+ for ( var i = 0; i < items.Length; i++ )
{
var item = items[i];
- var otherApiVersion = item.Model.GetAnnotationValue( item.Model )?.ApiVersion;
+ var otherApiVersion = item.Model.GetApiVersion();
if ( apiVersion.Equals( otherApiVersion ) )
{
@@ -459,6 +457,75 @@ private void UpdateModelTypes( ApiDescription description, IODataRoutingMetadata
}
}
+ private static void UpdateFunctionCollectionParameters( ApiDescription description, IODataRoutingMetadata metadata )
+ {
+ var parameters = description.ParameterDescriptions;
+
+ if ( parameters.Count == 0 )
+ {
+ return;
+ }
+
+ var function = default( IEdmFunction );
+ var mapping = default( IDictionary );
+
+ for ( var i = 0; i < metadata.Template.Count; i++ )
+ {
+ var segment = metadata.Template[i];
+
+ if ( segment is FunctionSegmentTemplate func )
+ {
+ function = func.Function;
+ mapping = func.ParameterMappings;
+ break;
+ }
+ else if ( segment is FunctionImportSegmentTemplate import )
+ {
+ function = import.FunctionImport.Function;
+ mapping = import.ParameterMappings;
+ break;
+ }
+ }
+
+ if ( function is null || mapping is null )
+ {
+ return;
+ }
+
+ var name = default( string );
+
+ foreach ( var parameter in function.Parameters )
+ {
+ if ( parameter.Type.IsCollection() &&
+ mapping.TryGetValue( parameter.Name, out name ) &&
+ parameters.SingleOrDefault( p => p.Name == name ) is { } param )
+ {
+ param.Source = BindingSource.Path;
+ break;
+ }
+ }
+
+ var path = description.RelativePath;
+
+ if ( string.IsNullOrEmpty( name ) || string.IsNullOrEmpty( path ) )
+ {
+ return;
+ }
+
+ var span = name.AsSpan();
+ Span oldValue = stackalloc char[name.Length + 2];
+ Span newValue = stackalloc char[name.Length + 4];
+
+ newValue[1] = oldValue[0] = '{';
+ newValue[^2] = oldValue[^1] = '}';
+ newValue[0] = '[';
+ newValue[^1] = ']';
+ span.CopyTo( oldValue.Slice( 1, name.Length ) );
+ span.CopyTo( newValue.Slice( 2, name.Length ) );
+
+ description.RelativePath = path.Replace( oldValue.ToString(), newValue.ToString(), Ordinal );
+ }
+
private sealed class ApiDescriptionComparer : IEqualityComparer
{
private readonly IEqualityComparer comparer = StringComparer.OrdinalIgnoreCase;
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs
new file mode 100644
index 00000000..5c73744b
--- /dev/null
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs
@@ -0,0 +1,19 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.ApiExplorer;
+
+using Asp.Versioning.OData;
+
+///
+/// Provides additional implementation specific to ASP.NET Core.
+///
+public partial class ODataApiExplorerOptions
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The associated model builder .
+ [CLSCompliant( false )]
+ public ODataApiExplorerOptions( VersionedODataModelBuilder modelBuilder ) =>
+ AdHocModelBuilder = modelBuilder ?? throw new ArgumentNullException( nameof( modelBuilder ) );
+}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptionsFactory.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptionsFactory.cs
new file mode 100644
index 00000000..5e494688
--- /dev/null
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptionsFactory.cs
@@ -0,0 +1,116 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.ApiExplorer;
+
+using Asp.Versioning.OData;
+using Microsoft.Extensions.Options;
+using static Asp.Versioning.ApiVersionMapping;
+
+///
+/// Represents a factory to create OData API explorer options.
+///
+[CLSCompliant( false )]
+public class ODataApiExplorerOptionsFactory : ApiExplorerOptionsFactory
+{
+ private readonly IApiVersionMetadataCollationProvider[] providers;
+ private readonly IEnumerable modelConfigurations;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// A sequence of
+ /// providers used to collate API version metadata.
+ /// A sequence of
+ /// configurations used to configure Entity Data Models.
+ /// The API versioning options
+ /// used to create API explorer options.
+ /// The sequence of
+ /// configuration actions to run.
+ /// The sequence of
+ /// initialization actions to run.
+ public ODataApiExplorerOptionsFactory(
+ IEnumerable providers,
+ IEnumerable modelConfigurations,
+ IOptions options,
+ IEnumerable> setups,
+ IEnumerable> postConfigures )
+ : base( options, setups, postConfigures )
+ {
+ this.providers = ( providers ?? throw new ArgumentNullException( nameof( providers ) ) ).ToArray();
+ this.modelConfigurations = modelConfigurations ?? throw new ArgumentNullException( nameof( modelConfigurations ) );
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// A sequence of
+ /// providers used to collate API version metadata.
+ /// A sequence of
+ /// configurations used to configure Entity Data Models.
+ /// The API versioning options
+ /// used to create API explorer options.
+ /// The sequence of
+ /// configuration actions to run.
+ /// The sequence of
+ /// initialization actions to run.
+ /// The sequence of
+ /// validations to run.
+ public ODataApiExplorerOptionsFactory(
+ IEnumerable providers,
+ IEnumerable modelConfigurations,
+ IOptions options,
+ IEnumerable> setups,
+ IEnumerable> postConfigures,
+ IEnumerable> validations )
+ : base( options, setups, postConfigures, validations )
+ {
+ this.providers = ( providers ?? throw new ArgumentNullException( nameof( providers ) ) ).ToArray();
+ this.modelConfigurations = modelConfigurations ?? throw new ArgumentNullException( nameof( modelConfigurations ) );
+ }
+
+ ///
+ protected override ODataApiExplorerOptions CreateInstance( string name )
+ {
+ var options = new ODataApiExplorerOptions( new( CollateApiVersions( providers, Options ), modelConfigurations ) );
+ CopyOptions( Options, options );
+ return options;
+ }
+
+ private static ODataApiVersionCollectionProvider CollateApiVersions(
+ IApiVersionMetadataCollationProvider[] providers,
+ ApiVersioningOptions options )
+ {
+ var context = new ApiVersionMetadataCollationContext();
+
+ for ( var i = 0; i < providers.Length; i++ )
+ {
+ providers[i].Execute( context );
+ }
+
+ var results = context.Results;
+ var versions = new SortedSet();
+
+ for ( var i = 0; i < results.Count; i++ )
+ {
+ var model = results[i].Map( Implicit );
+ var declared = model.DeclaredApiVersions;
+
+ for ( var j = 0; j < declared.Count; j++ )
+ {
+ versions.Add( declared[j] );
+ }
+ }
+
+ if ( versions.Count == 0 )
+ {
+ versions.Add( options.DefaultApiVersion );
+ }
+
+ return new() { ApiVersions = versions.ToArray() };
+ }
+
+ private sealed class ODataApiVersionCollectionProvider : IODataApiVersionCollectionProvider
+ {
+ public required IReadOnlyList ApiVersions { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataQueryOptionModelMetadata.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataQueryOptionModelMetadata.cs
index 062a95aa..bc2a8825 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataQueryOptionModelMetadata.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataQueryOptionModelMetadata.cs
@@ -23,10 +23,7 @@ public sealed class ODataQueryOptionModelMetadata : ModelMetadata
public ODataQueryOptionModelMetadata( IModelMetadataProvider modelMetadataProvider, Type modelType, string description )
: base( ModelMetadataIdentity.ForType( modelType ) )
{
- if ( modelMetadataProvider == null )
- {
- throw new ArgumentNullException( nameof( modelMetadataProvider ) );
- }
+ ArgumentNullException.ThrowIfNull( modelMetadataProvider );
inner = modelMetadataProvider.GetMetadataForType( modelType );
Description = description;
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs
new file mode 100644
index 00000000..47eac901
--- /dev/null
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs
@@ -0,0 +1,233 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.ApiExplorer;
+
+using Asp.Versioning;
+using Asp.Versioning.Conventions;
+using Asp.Versioning.OData;
+using Microsoft.AspNetCore.Mvc.Abstractions;
+using Microsoft.AspNetCore.Mvc.ApiExplorer;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Microsoft.AspNetCore.OData;
+using Microsoft.AspNetCore.OData.Routing;
+using Microsoft.AspNetCore.OData.Routing.Template;
+using Microsoft.Extensions.Options;
+using Microsoft.OData.Edm;
+using Microsoft.OData.ModelBuilder;
+using System.Runtime.CompilerServices;
+using Opts = Microsoft.Extensions.Options.Options;
+
+///
+/// Represents an API description provider for partial OData support.
+///
+[CLSCompliant( false )]
+public class PartialODataDescriptionProvider : IApiDescriptionProvider
+{
+ private static readonly int BeforeOData = ODataOrder() + 10;
+ private readonly IOptionsFactory odataOptionsFactory;
+ private readonly IOptions options;
+ private bool markedAdHoc;
+ private IODataQueryOptionsConvention[]? conventions;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The factory used to create
+ /// OData options .
+ /// The container of configured
+ /// API explorer options .
+ public PartialODataDescriptionProvider(
+ IOptionsFactory odataOptionsFactory,
+ IOptions options )
+ {
+ this.odataOptionsFactory = odataOptionsFactory ?? throw new ArgumentNullException( nameof( odataOptionsFactory ) );
+ this.options = options ?? throw new ArgumentNullException( nameof( options ) );
+ }
+
+ ///
+ /// Gets the associated OData API explorer options.
+ ///
+ /// The current OData API explorer options .
+ protected ODataApiExplorerOptions Options
+ {
+ get
+ {
+ var value = options.Value;
+
+ if ( !markedAdHoc )
+ {
+ value.AdHocModelBuilder.OnModelCreated += MarkAsAdHoc;
+ markedAdHoc = true;
+ }
+
+ return value;
+ }
+ }
+
+ ///
+ /// Gets the builder used to create ad hoc Entity Data Models (EDMs).
+ ///
+ /// The associated model builder .
+ protected VersionedODataModelBuilder ModelBuilder => Options.AdHocModelBuilder;
+
+ ///
+ /// Gets associated the OData query option conventions.
+ ///
+ /// A read-only list of
+ /// OData query option conventions .
+ protected IReadOnlyList Conventions =>
+ conventions ??= Options.AdHocModelBuilder.ModelConfigurations.OfType().ToArray();
+
+ ///
+ /// Gets or sets the order precedence of the current API description provider.
+ ///
+ /// The order precedence of the current API description provider.
+ public int Order { get; protected set; } = BeforeOData;
+
+ ///
+ public virtual void OnProvidersExecuting( ApiDescriptionProviderContext context )
+ {
+ ArgumentNullException.ThrowIfNull( context );
+
+ var results = FilterResults( context.Results, Conventions );
+
+ if ( results.Length == 0 )
+ {
+ return;
+ }
+
+ var models = ModelBuilder.GetEdmModels();
+
+ for ( var i = 0; i < models.Count; i++ )
+ {
+ var model = models[i];
+ var version = model.GetApiVersion();
+ var odata = odataOptionsFactory.Create( Opts.DefaultName );
+
+ odata.AddRouteComponents( model );
+
+ for ( var j = 0; j < results.Length; j++ )
+ {
+ var result = results[j];
+ var metadata = result.ActionDescriptor.GetApiVersionMetadata();
+
+ if ( metadata.IsMappedTo( version ) )
+ {
+ result.ActionDescriptor.EndpointMetadata.Add( ODataMetadata.New( model ) );
+ }
+ }
+ }
+ }
+
+ ///
+ public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context )
+ {
+ ArgumentNullException.ThrowIfNull( context );
+
+ var actions = context.Actions;
+
+ for ( var i = 0; i < actions.Count; i++ )
+ {
+ var metadata = actions[i].EndpointMetadata;
+
+ for ( var j = metadata.Count - 1; j >= 0; j-- )
+ {
+ if ( metadata[j] is IODataRoutingMetadata routing && routing.Model.IsAdHoc() )
+ {
+ metadata.Remove( j );
+ }
+ }
+ }
+ }
+
+ [MethodImpl( MethodImplOptions.AggressiveInlining )]
+ private static int ODataOrder() =>
+ new ODataApiDescriptionProvider(
+ new StubModelMetadataProvider(),
+ new StubModelTypeBuilder(),
+ new OptionsFactory( [], [] ),
+ Opts.Create(
+ new ODataApiExplorerOptions(
+ new( new StubODataApiVersionCollectionProvider(), [] ) ) ) ).Order;
+
+ [MethodImpl( MethodImplOptions.AggressiveInlining )]
+ private static void MarkAsAdHoc( ODataModelBuilder builder, IEdmModel model ) =>
+ model.SetAnnotationValue( model, AdHocAnnotation.Instance );
+
+ private static ApiDescription[] FilterResults(
+ IList results,
+ IReadOnlyList conventions )
+ {
+ var filtered = default( List );
+
+ for ( var i = 0; i < results.Count; i++ )
+ {
+ var result = results[i];
+ var metadata = result.ActionDescriptor.EndpointMetadata;
+ var odata = false;
+
+ for ( var j = 0; j < metadata.Count; j++ )
+ {
+ if ( metadata[j] is IODataRoutingMetadata )
+ {
+ odata = true;
+ break;
+ }
+ }
+
+ if ( odata || !result.IsODataLike() )
+ {
+ continue;
+ }
+
+ filtered ??= new( capacity: results.Count );
+ filtered.Add( result );
+
+ for ( var j = 0; j < conventions.Count; j++ )
+ {
+ conventions[j].ApplyTo( result );
+ }
+ }
+
+ return filtered?.ToArray() ?? [];
+ }
+
+ private sealed class StubModelMetadataProvider : IModelMetadataProvider
+ {
+ public IEnumerable GetMetadataForProperties( Type modelType ) =>
+ throw new NotImplementedException();
+
+ public ModelMetadata GetMetadataForType( Type modelType ) =>
+ throw new NotImplementedException();
+ }
+
+ private sealed class StubModelTypeBuilder : IModelTypeBuilder
+ {
+ public Type NewActionParameters( IEdmModel model, IEdmAction action, string controllerName, ApiVersion apiVersion ) =>
+ throw new NotImplementedException();
+
+ public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredType, Type clrType, ApiVersion apiVersion ) =>
+ throw new NotImplementedException();
+ }
+
+ private sealed class StubODataApiVersionCollectionProvider : IODataApiVersionCollectionProvider
+ {
+ public IReadOnlyList ApiVersions
+ {
+ get => throw new NotImplementedException();
+ set => throw new NotImplementedException();
+ }
+ }
+
+ private static class ODataMetadata
+ {
+ private const string ArbitrarySegment = "52459ff8-bca1-4a26-b7f2-08c7da04472d";
+
+ // metadata (~/$metadata) and service (~/) doc have special handling.
+ // make sure we don't match the service doc
+ private static readonly ODataPathTemplate AdHocODataTemplate =
+ new( new DynamicSegmentTemplate( new( ArbitrarySegment ) ) );
+
+ public static ODataRoutingMetadata New( IEdmModel model ) => new( string.Empty, model, AdHocODataTemplate );
+ }
+}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/SubstitutedModelMetadata.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/SubstitutedModelMetadata.cs
index ec670cde..5ee9b575 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/SubstitutedModelMetadata.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/SubstitutedModelMetadata.cs
@@ -1,5 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+#pragma warning disable IDE0079
#pragma warning disable CA1812
namespace Asp.Versioning.ApiExplorer;
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj
index 36b8c467..a443fa16 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj
@@ -1,9 +1,9 @@
- 7.0.0
- 7.0.0.0
- net7.0
+ 8.2.0
+ 8.2.0.0
+ $(DefaultTargetFramework)
Asp.Versioning
ASP.NET Core API Versioning API Explorer for OData v4.0
The API Explorer extensions for ASP.NET Core API Versioning and OData v4.0.
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs
new file mode 100644
index 00000000..6ca75048
--- /dev/null
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs
@@ -0,0 +1,43 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.Conventions;
+
+using Microsoft.AspNetCore.Mvc.ApiExplorer;
+
+///
+/// Provides additional implementation specific to ASP.NET Core.
+///
+[CLSCompliant( false )]
+public partial class ImplicitModelBoundSettingsConvention
+{
+ ///
+ public void ApplyTo( ApiDescription apiDescription )
+ {
+ ArgumentNullException.ThrowIfNull( apiDescription );
+
+ var responses = apiDescription.SupportedResponseTypes;
+
+ for ( var j = 0; j < responses.Count; j++ )
+ {
+ var response = responses[j];
+ var notForSuccess = response.StatusCode < 200 || response.StatusCode >= 300;
+
+ if ( notForSuccess )
+ {
+ continue;
+ }
+
+ var model = response.ModelMetadata;
+ var type = model == null
+ ? response.Type
+ : model.IsEnumerableType
+ ? model.ElementType
+ : model.UnderlyingOrModelType;
+
+ if ( type != null )
+ {
+ types.Add( type );
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs
index aef2fdbf..ae3ad97b 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs
@@ -34,7 +34,7 @@ public partial class ODataQueryOptionDescriptionContext
foreach ( var item in items )
{
var model = item.Model;
- var otherVersion = model.GetAnnotationValue( model )?.ApiVersion;
+ var otherVersion = model.GetApiVersion();
if ( version.Equals( otherVersion ) )
{
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs
index 9a91c9fa..3c876e07 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs
@@ -4,6 +4,7 @@ namespace Asp.Versioning.Conventions;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
+using System.Reflection;
///
/// Provides additional implementation specific to Microsoft ASP.NET Core.
@@ -11,28 +12,13 @@ namespace Asp.Versioning.Conventions;
[CLSCompliant( false )]
public partial class ODataQueryOptionsConventionBuilder
{
- private static Type GetController( ApiDescription apiDescription )
+ private static TypeInfo GetController( ApiDescription apiDescription )
{
if ( apiDescription.ActionDescriptor is ControllerActionDescriptor action )
{
return action.ControllerTypeInfo;
}
- return typeof( object );
- }
-
- private static bool IsODataLike( ApiDescription description )
- {
- var parameters = description.ActionDescriptor.Parameters;
-
- for ( var i = 0; i < parameters.Count; i++ )
- {
- if ( parameters[i].ParameterType.IsODataQueryOptions() )
- {
- return true;
- }
- }
-
- return false;
+ return typeof( object ).GetTypeInfo();
}
}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs
index cef80319..bfab96ce 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs
@@ -16,10 +16,7 @@ public partial class ODataValidationSettingsConvention
///
public virtual void ApplyTo( ApiDescription apiDescription )
{
- if ( apiDescription == null )
- {
- throw new ArgumentNullException( nameof( apiDescription ) );
- }
+ ArgumentNullException.ThrowIfNull( apiDescription );
if ( !IsSupported( apiDescription.HttpMethod ) )
{
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs
index 27283ec7..9304fe5d 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs
@@ -4,6 +4,7 @@ namespace Microsoft.Extensions.DependencyInjection;
using Asp.Versioning;
using Asp.Versioning.ApiExplorer;
+using Asp.Versioning.Conventions;
using Asp.Versioning.OData;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.DependencyInjection.Extensions;
@@ -23,11 +24,7 @@ public static class IApiVersioningBuilderExtensions
/// The original .
public static IApiVersioningBuilder AddODataApiExplorer( this IApiVersioningBuilder builder )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
AddApiExplorerServices( builder );
return builder;
}
@@ -40,11 +37,7 @@ public static IApiVersioningBuilder AddODataApiExplorer( this IApiVersioningBuil
/// The original .
public static IApiVersioningBuilder AddODataApiExplorer( this IApiVersioningBuilder builder, Action setupAction )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
AddApiExplorerServices( builder );
builder.Services.Configure( setupAction );
return builder;
@@ -55,21 +48,21 @@ private static void AddApiExplorerServices( IApiVersioningBuilder builder )
var services = builder.Services;
builder.AddApiExplorer();
+ builder.Services.AddModelConfigurationsAsServices();
services.TryAddSingleton();
- services.TryAddSingleton, ApiExplorerOptionsFactory>();
+ services.TryAddSingleton, ODataApiExplorerOptionsFactory>();
+ services.TryAddEnumerable( Transient() );
services.TryAddEnumerable( Transient() );
+ services.TryAddEnumerable( Transient() );
services.Replace( Singleton, ODataApiExplorerOptionsAdapter>() );
}
+#pragma warning disable IDE0079
#pragma warning disable CA1812
- private sealed class ODataApiExplorerOptionsAdapter : IOptionsFactory
+ private sealed class ODataApiExplorerOptionsAdapter( IOptionsFactory factory )
+ : IOptionsFactory
{
- private readonly IOptionsFactory factory;
-
- public ODataApiExplorerOptionsAdapter( IOptionsFactory factory ) =>
- this.factory = factory;
-
public ApiExplorerOptions Create( string name ) => factory.Create( name );
}
}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Format.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Format.cs
new file mode 100644
index 00000000..11126284
--- /dev/null
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Format.cs
@@ -0,0 +1,22 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+using System.Text;
+
+internal static class Format
+{
+ internal static readonly CompositeFormat UnsupportedQueryOption = CompositeFormat.Parse( ODataExpSR.UnsupportedQueryOption );
+ internal static readonly CompositeFormat MaxExpressionDesc = CompositeFormat.Parse( ODataExpSR.MaxExpressionDesc );
+ internal static readonly CompositeFormat AllowedPropertiesDesc = CompositeFormat.Parse( ODataExpSR.AllowedPropertiesDesc );
+ internal static readonly CompositeFormat MaxDepthDesc = CompositeFormat.Parse( ODataExpSR.MaxDepthDesc );
+ internal static readonly CompositeFormat MaxValueDesc = CompositeFormat.Parse( ODataExpSR.MaxValueDesc );
+ internal static readonly CompositeFormat AllowedLogicalOperatorsDesc = CompositeFormat.Parse( ODataExpSR.AllowedLogicalOperatorsDesc );
+ internal static readonly CompositeFormat AllowedArithmeticOperatorsDesc = CompositeFormat.Parse( ODataExpSR.AllowedArithmeticOperatorsDesc );
+ internal static readonly CompositeFormat AmbiguousActionMethod = CompositeFormat.Parse( ODataExpSR.AmbiguousActionMethod );
+ internal static readonly CompositeFormat InvalidActionMethodExpression = CompositeFormat.Parse( ODataExpSR.InvalidActionMethodExpression );
+ internal static readonly CompositeFormat ActionMethodNotFound = CompositeFormat.Parse( ODataExpSR.ActionMethodNotFound );
+ internal static readonly CompositeFormat AllowedFunctionsDesc = CompositeFormat.Parse( ODataExpSR.AllowedFunctionsDesc );
+ internal static readonly CompositeFormat RequiredInterfaceNotImplemented = CompositeFormat.Parse( ODataExpSR.RequiredInterfaceNotImplemented );
+ internal static readonly CompositeFormat ConventionStyleMismatch = CompositeFormat.Parse( ODataExpSR.ConventionStyleMismatch );
+}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt
index 5f282702..b5318606 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt
@@ -1 +1 @@
-
\ No newline at end of file
+Support OData 9.0 ([#1103](https://github.com/dotnet/aspnet-api-versioning/issues/1103))
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/ApplicationModels/ODataControllerSpecification.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/ApplicationModels/ODataControllerSpecification.cs
index 2a778ee8..eb1cebd0 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/ApplicationModels/ODataControllerSpecification.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/ApplicationModels/ODataControllerSpecification.cs
@@ -14,10 +14,7 @@ public sealed class ODataControllerSpecification : IApiControllerSpecification
///
public bool IsSatisfiedBy( ControllerModel controller )
{
- if ( controller == null )
- {
- throw new ArgumentNullException( nameof( controller ) );
- }
+ ArgumentNullException.ThrowIfNull( controller );
if ( ODataControllerSpecification.IsSatisfiedBy( controller ) )
{
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj
index 55e90b91..e69c80d7 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj
@@ -1,9 +1,9 @@
- 7.0.0
- 7.0.0.0
- net7.0
+ 8.2.0
+ 8.2.0.0
+ $(DefaultTargetFramework)
Asp.Versioning
ASP.NET Core API Versioning with OData v4.0
A service API versioning library for Microsoft ASP.NET Core with OData v4.0.
@@ -15,7 +15,7 @@
-
+
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Builder/IApplicationBuilderExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/Builder/IApplicationBuilderExtensions.cs
index 0ce54373..c6248429 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Builder/IApplicationBuilderExtensions.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Builder/IApplicationBuilderExtensions.cs
@@ -17,11 +17,7 @@ public static class IApplicationBuilderExtensions
/// The original .
public static IApplicationBuilder UseVersionedODataBatching( this IApplicationBuilder app )
{
- if ( app == null )
- {
- throw new ArgumentNullException( nameof( app ) );
- }
-
+ ArgumentNullException.ThrowIfNull( app );
return app.UseMiddleware();
}
}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Controllers/VersionedMetadataController.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/Controllers/VersionedMetadataController.cs
index e29e2ede..1f4b0266 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Controllers/VersionedMetadataController.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Controllers/VersionedMetadataController.cs
@@ -4,7 +4,6 @@ namespace Asp.Versioning.Controllers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OData.Routing.Controllers;
-using Microsoft.Extensions.Primitives;
using static Microsoft.OData.ODataConstants;
using static Microsoft.OData.ODataUtils;
using static Microsoft.OData.ODataVersion;
@@ -17,6 +16,8 @@ namespace Asp.Versioning.Controllers;
[ControllerName( "OData" )]
public class VersionedMetadataController : MetadataController
{
+ private static readonly string[] values = ["GET", "OPTIONS"];
+
///
/// Handles a request for the HTTP OPTIONS method.
///
@@ -55,8 +56,8 @@ public virtual IActionResult GetOptions()
{
var headers = Response.Headers;
- headers.Add( "Allow", new StringValues( new[] { "GET", "OPTIONS" } ) );
- headers.Add( ODataVersionHeader, ODataVersionToString( V4 ) );
+ headers.Allow = new( values );
+ headers[ODataVersionHeader] = ODataVersionToString( V4 );
return Ok();
}
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs
index 3af0b775..8d90b695 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs
@@ -9,7 +9,6 @@ namespace Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
-using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.OData;
using Microsoft.AspNetCore.OData.Routing.Template;
using Microsoft.AspNetCore.Routing;
@@ -32,11 +31,7 @@ public static class IApiVersioningBuilderExtensions
/// The original .
public static IApiVersioningBuilder AddOData( this IApiVersioningBuilder builder )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
-
+ ArgumentNullException.ThrowIfNull( builder );
AddServices( builder.AddMvc().Services );
return builder;
}
@@ -50,10 +45,7 @@ public static IApiVersioningBuilder AddOData( this IApiVersioningBuilder builder
[CLSCompliant( false )]
public static IApiVersioningBuilder AddOData( this IApiVersioningBuilder builder, Action setupAction )
{
- if ( builder == null )
- {
- throw new ArgumentNullException( nameof( builder ) );
- }
+ ArgumentNullException.ThrowIfNull( builder );
var services = builder.AddMvc().Services;
AddServices( services );
@@ -66,8 +58,7 @@ private static void AddServices( IServiceCollection services )
services.TryRemoveODataService( typeof( IApplicationModelProvider ), ODataRoutingApplicationModelProviderType );
var partManager = services.GetOrCreateApplicationPartManager();
-
- ConfigureDefaultFeatureProviders( partManager );
+ var configured = partManager.ConfigureDefaultFeatureProviders();
services.AddHttpContextAccessor();
services.TryAddSingleton();
@@ -84,24 +75,11 @@ private static void AddServices( IServiceCollection services )
services.TryAddEnumerable( Singleton() );
services.TryAddEnumerable( Transient() );
services.TryAddEnumerable( Transient() );
- services.AddModelConfigurationsAsServices( partManager );
- }
- private static T GetService( this IServiceCollection services ) =>
- (T) services.LastOrDefault( d => d.ServiceType == typeof( T ) )?.ImplementationInstance!;
-
- private static ApplicationPartManager GetOrCreateApplicationPartManager( this IServiceCollection services )
- {
- var partManager = services.GetService();
-
- if ( partManager == null )
+ if ( configured )
{
- partManager = new ApplicationPartManager();
- services.TryAddSingleton( partManager );
+ services.AddModelConfigurationsAsServices( partManager );
}
-
- partManager.ApplicationParts.Add( new AssemblyPart( typeof( ODataApiVersioningOptions ).Assembly ) );
- return partManager;
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
@@ -124,7 +102,13 @@ private static void TryRemoveODataService( this IServiceCollection services, Typ
}
}
- var message = string.Format( CultureInfo.CurrentCulture, SR.UnableToFindServices, nameof( IMvcBuilder ), "AddOData", "ConfigureServices(...)" );
+ var message = string.Format(
+ CultureInfo.CurrentCulture,
+ Format.UnableToFindServices,
+ nameof( IMvcBuilder ),
+ "AddOData",
+ "ConfigureServices(...)" );
+
throw new InvalidOperationException( message );
}
@@ -148,27 +132,6 @@ private static void TryReplaceODataService(
}
}
- private static void AddModelConfigurationsAsServices( this IServiceCollection services, ApplicationPartManager partManager )
- {
- var feature = new ModelConfigurationFeature();
- var modelConfigurationType = typeof( IModelConfiguration );
-
- partManager.PopulateFeature( feature );
-
- foreach ( var modelConfiguration in feature.ModelConfigurations )
- {
- services.TryAddEnumerable( Transient( modelConfigurationType, modelConfiguration ) );
- }
- }
-
- private static void ConfigureDefaultFeatureProviders( ApplicationPartManager partManager )
- {
- if ( !partManager.FeatureProviders.OfType().Any() )
- {
- partManager.FeatureProviders.Add( new ModelConfigurationFeatureProvider() );
- }
- }
-
private static object CreateInstance( this IServiceProvider services, ServiceDescriptor descriptor )
{
if ( descriptor.ImplementationInstance != null )
@@ -198,12 +161,8 @@ IHttpContextFactory NewFactory( IServiceProvider serviceProvider )
return Describe( typeof( IHttpContextFactory ), NewFactory, lifetime );
}
- private sealed class HttpContextFactoryDecorator : IHttpContextFactory
+ private sealed class HttpContextFactoryDecorator( IHttpContextFactory decorated ) : IHttpContextFactory
{
- private readonly IHttpContextFactory decorated;
-
- public HttpContextFactoryDecorator( IHttpContextFactory decorated ) => this.decorated = decorated;
-
public HttpContext Create( IFeatureCollection featureCollection )
{
// features do not support cloning or DI, which is precisely why ASP.NET Core no longer supports
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IServiceCollectionExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IServiceCollectionExtensions.cs
new file mode 100644
index 00000000..16432665
--- /dev/null
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IServiceCollectionExtensions.cs
@@ -0,0 +1,73 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+using Asp.Versioning.OData;
+using Microsoft.AspNetCore.Mvc.ApplicationParts;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using System.Runtime.CompilerServices;
+using static Microsoft.Extensions.DependencyInjection.ServiceDescriptor;
+
+///
+/// Provides extension methods for .
+///
+public static class IServiceCollectionExtensions
+{
+ [MethodImpl( MethodImplOptions.AggressiveInlining )]
+ internal static T GetService( this IServiceCollection services ) =>
+ (T) services.LastOrDefault( d => d.ServiceType == typeof( T ) )?.ImplementationInstance!;
+
+ internal static ApplicationPartManager GetOrCreateApplicationPartManager( this IServiceCollection services )
+ {
+ var partManager = services.GetService();
+
+ if ( partManager == null )
+ {
+ partManager = new ApplicationPartManager();
+ services.TryAddSingleton( partManager );
+ }
+
+ partManager.ApplicationParts.Add( new AssemblyPart( typeof( ODataApiVersioningOptions ).Assembly ) );
+ return partManager;
+ }
+
+ internal static void AddModelConfigurationsAsServices( this IServiceCollection services, ApplicationPartManager partManager )
+ {
+ var feature = new ModelConfigurationFeature();
+ var modelConfigurationType = typeof( IModelConfiguration );
+
+ partManager.PopulateFeature( feature );
+
+ foreach ( var modelConfiguration in feature.ModelConfigurations )
+ {
+ services.TryAddEnumerable( Transient( modelConfigurationType, modelConfiguration ) );
+ }
+ }
+
+ internal static bool ConfigureDefaultFeatureProviders( this ApplicationPartManager partManager )
+ {
+ if ( partManager.FeatureProviders.OfType().Any() )
+ {
+ return false;
+ }
+
+ partManager.FeatureProviders.Add( new ModelConfigurationFeatureProvider() );
+ return true;
+ }
+
+ ///
+ /// Registers discovered model configurations as services in the .
+ ///
+ /// The extended .
+ public static void AddModelConfigurationsAsServices( this IServiceCollection services )
+ {
+ ArgumentNullException.ThrowIfNull( services );
+
+ var partManager = services.GetOrCreateApplicationPartManager();
+
+ if ( ConfigureDefaultFeatureProviders( partManager ) )
+ {
+ services.AddModelConfigurationsAsServices( partManager );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Format.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/Format.cs
new file mode 100644
index 00000000..c4202f8c
--- /dev/null
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Format.cs
@@ -0,0 +1,10 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+using System.Text;
+
+internal static class Format
+{
+ internal static readonly CompositeFormat UnableToFindServices = CompositeFormat.Parse( SR.UnableToFindServices );
+}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs
index dede3b74..8cf589be 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs
@@ -26,7 +26,7 @@ public void Add( string prefixName, string routeTemplate, ODataBatchHandler hand
Debug.Assert( count < mappings.Length, "The capacity has been exceeded." );
var template = TemplateParser.Parse( routeTemplate.TrimStart( '/' ) );
- var matcher = new TemplateMatcher( template, new() );
+ var matcher = new TemplateMatcher( template, [] );
handler.PrefixName = prefixName;
mappings[count++] = (matcher, handler, version);
@@ -40,11 +40,56 @@ public bool TryGetHandler( HttpContext context, [NotNullWhen( true )] out ODataB
return false;
}
+ var routeData = new RouteValueDictionary();
+ var candidates = new Dictionary( capacity: mappings.Length );
+
+ batchHandler = SelectExactMatch( context, routeData, candidates ) ??
+ SelectBestCandidate( context, candidates, routeData );
+
+ return batchHandler is not null;
+ }
+
+ public ValueTask TryGetHandlerAsync( HttpContext context, CancellationToken cancellationToken )
+ {
+ if ( count == 0 )
+ {
+ return ValueTask.FromResult( default( ODataBatchHandler ) );
+ }
+
+ var routeData = new RouteValueDictionary();
+ var candidates = new Dictionary( capacity: mappings.Length );
+
+ if ( SelectExactMatch( context, routeData, candidates ) is var handler )
+ {
+ return ValueTask.FromResult( handler );
+ }
+
+ return SelectBestCandidateAsync( context, candidates, routeData, cancellationToken );
+ }
+
+ private static void MergeRouteData( HttpContext context, RouteValueDictionary routeData )
+ {
+ if ( routeData.Count == 0 )
+ {
+ return;
+ }
+
+ var batchRouteData = context.ODataFeature().BatchRouteData;
+
+ foreach ( var (key, value) in routeData )
+ {
+ batchRouteData.Add( key, value );
+ }
+ }
+
+ private ODataBatchHandler? SelectExactMatch(
+ HttpContext context,
+ RouteValueDictionary routeData,
+ Dictionary candidates )
+ {
var path = context.Request.Path;
var feature = context.ApiVersioningFeature();
var unspecified = feature.RawRequestedApiVersions.Count == 0;
- var routeData = new RouteValueDictionary();
- var candidates = new Dictionary( capacity: mappings.Length );
for ( var i = 0; i < count; i++ )
{
@@ -73,33 +118,40 @@ public bool TryGetHandler( HttpContext context, [NotNullWhen( true )] out ODataB
}
MergeRouteData( context, routeData );
- batchHandler = handler;
- return true;
+ return handler;
}
- batchHandler = SelectBestCandidate( context, ref path, candidates, routeData );
- return batchHandler is not null;
+ return default;
}
- private static void MergeRouteData( HttpContext context, RouteValueDictionary routeData )
+ private ODataBatchHandler? SelectBestCandidate(
+ HttpContext context,
+ Dictionary candidates,
+ RouteValueDictionary routeData,
+ ApiVersion version )
{
- if ( routeData.Count == 0 )
+ if ( version is null || !candidates.TryGetValue( version, out var index ) )
{
- return;
+ return default;
}
- var batchRouteData = context.ODataFeature().BatchRouteData;
+ ref readonly var mapping = ref mappings[index];
+ var (matcher, handler, _) = mapping;
- foreach ( var (key, value) in routeData )
- {
- batchRouteData.Add( key, value );
- }
+ routeData.Clear();
+ matcher.TryMatch( context.Request.Path, routeData );
+ MergeRouteData( context, routeData );
+
+ // it's important that the resolved api version be set here to ensure the correct
+ // ODataOptions are resolved by ODataBatchHandler when executed
+ context.ApiVersioningFeature().RequestedApiVersion = version;
+
+ return handler;
}
private ODataBatchHandler? SelectBestCandidate(
HttpContext context,
- ref PathString path,
- IReadOnlyDictionary candidates,
+ Dictionary candidates,
RouteValueDictionary routeData )
{
if ( candidates.Count == 0 )
@@ -114,22 +166,27 @@ private static void MergeRouteData( HttpContext context, RouteValueDictionary ro
var model = new ApiVersionModel( candidates.Keys, Enumerable.Empty() );
var version = selector.SelectVersion( context.Request, model );
- if ( version is null || !candidates.TryGetValue( version, out var index ) )
+ return SelectBestCandidate( context, candidates, routeData, version );
+ }
+
+ private async ValueTask SelectBestCandidateAsync(
+ HttpContext context,
+ Dictionary candidates,
+ RouteValueDictionary routeData,
+ CancellationToken cancellationToken )
+ {
+ if ( candidates.Count == 0 )
{
return default;
}
- ref readonly var mapping = ref mappings[index];
- var (matcher, handler, _) = mapping;
-
- routeData.Clear();
- matcher.TryMatch( path, routeData );
- MergeRouteData( context, routeData );
-
- // it's important that the resolved api version be set here to ensure the correct
- // ODataOptions are resolved by ODataBatchHandler when executed
- context.ApiVersioningFeature().RequestedApiVersion = version;
+ // ~/$batch is always version-neutral so there is no need to check
+ // ApiVersioningOptions.AllowDefaultVersionWhenUnspecified. use the
+ // configured IApiVersionSelector to provide a chance to select the
+ // most appropriate version.
+ var model = new ApiVersionModel( candidates.Keys, Enumerable.Empty() );
+ var version = await selector.SelectVersionAsync( context.Request, model, cancellationToken ).ConfigureAwait( false );
- return handler;
+ return SelectBestCandidate( context, candidates, routeData, version );
}
}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/VersionedODataBatchMiddleware.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/VersionedODataBatchMiddleware.cs
index 1eff0220..df89490d 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/VersionedODataBatchMiddleware.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/VersionedODataBatchMiddleware.cs
@@ -31,10 +31,7 @@ public VersionedODataBatchMiddleware( RequestDelegate next, VersionedODataOption
/// A task representing the asynchronous operation.
public Task Invoke( HttpContext context )
{
- if ( context == null )
- {
- throw new ArgumentNullException( nameof( context ) );
- }
+ ArgumentNullException.ThrowIfNull( context );
if ( HttpMethods.IsPost( context.Request.Method ) &&
options.TryGetBatchHandler( context, out var handler ) )
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeature.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeature.cs
index 95a2cdc7..5ab6b579 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeature.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeature.cs
@@ -21,5 +21,5 @@ public class ModelConfigurationFeature
/// Gets the collection of model configurations in an application.
///
/// The collection of model configurations in an application.
- public ICollection ModelConfigurations => modelConfigurations ??= new();
+ public ICollection ModelConfigurations => modelConfigurations ??= [];
}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeatureProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeatureProvider.cs
index 7f6254b3..66cf48e9 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeatureProvider.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeatureProvider.cs
@@ -15,15 +15,8 @@ public class ModelConfigurationFeatureProvider : IApplicationFeatureProvider
public void PopulateFeature( IEnumerable parts, ModelConfigurationFeature feature )
{
- if ( parts == null )
- {
- throw new ArgumentNullException( nameof( parts ) );
- }
-
- if ( feature == null )
- {
- throw new ArgumentNullException( nameof( feature ) );
- }
+ ArgumentNullException.ThrowIfNull( parts );
+ ArgumentNullException.ThrowIfNull( feature );
var types = from part in parts.OfType()
from type in part.Types
@@ -43,10 +36,7 @@ where IsModelConfiguration( type )
/// True if the type is a model configuration ; otherwise false .
protected virtual bool IsModelConfiguration( Type type )
{
- if ( type == null )
- {
- throw new ArgumentNullException( nameof( type ) );
- }
+ ArgumentNullException.ThrowIfNull( type );
if ( !type.IsClass || type.IsAbstract || !type.IsPublic || type.ContainsGenericParameters )
{
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApiVersionCollectionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApiVersionCollectionProvider.cs
index 76cada9f..0aa16562 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApiVersionCollectionProvider.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApiVersionCollectionProvider.cs
@@ -1,5 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+#pragma warning disable IDE0079
#pragma warning disable CA1812
namespace Asp.Versioning.OData;
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs
index 0d9cdcc0..9ef71b7b 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs
@@ -62,10 +62,7 @@ public virtual void OnProvidersExecuted( ApplicationModelProviderContext context
///
public virtual void OnProvidersExecuting( ApplicationModelProviderContext context )
{
- if ( context == null )
- {
- throw new ArgumentNullException( nameof( context ) );
- }
+ ArgumentNullException.ThrowIfNull( context );
var (metadataControllers, supported, deprecated) = CollateApiVersions( context.Result );
@@ -92,7 +89,7 @@ private static
if ( controller.ControllerType.IsMetadataController() )
{
- metadataControllers ??= new();
+ metadataControllers ??= [];
metadataControllers.Add( controller );
continue;
}
@@ -119,7 +116,7 @@ private static
if ( supported == null && versions.Count > 0 )
{
- supported = new();
+ supported = [];
}
for ( var k = 0; k < versions.Count; k++ )
@@ -131,7 +128,7 @@ private static
if ( deprecated == null && versions.Count > 0 )
{
- deprecated = new();
+ deprecated = [];
}
for ( var k = 0; k < versions.Count; k++ )
@@ -144,7 +141,7 @@ private static
return (metadataControllers, supported, deprecated);
}
- private static ControllerModel? SelectBestMetadataController( IReadOnlyList controllers )
+ private static ControllerModel? SelectBestMetadataController( List controllers )
{
// note: there should be at least 2 metadata controllers, but there could be 3+
// if a developer defines their own custom controller. ultimately, there can be
@@ -223,7 +220,7 @@ private void ApplyMetadataControllerConventions(
builder.ApplyTo( metadataController );
}
- private IReadOnlyList MergeApiVersions(
+ private ApiVersion[] MergeApiVersions(
SortedSet? supported,
SortedSet? deprecated )
{
@@ -231,14 +228,14 @@ private IReadOnlyList MergeApiVersions(
{
if ( supported == null )
{
- return new[] { Options.DefaultApiVersion };
+ return [Options.DefaultApiVersion];
}
- return supported.ToArray();
+ return [.. supported];
}
else if ( supported == null )
{
- return deprecated.ToArray();
+ return [.. deprecated];
}
return supported.Union( deprecated ).ToArray();
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs
index c8fc3fe0..cc798280 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs
@@ -1,10 +1,10 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+#pragma warning disable IDE0079
#pragma warning disable CA1812
namespace Asp.Versioning.OData;
-using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.OData;
using Microsoft.AspNetCore.OData.Routing.Conventions;
@@ -153,7 +153,11 @@ private void AddRouteComponents(
for ( var i = 0; i < models.Count; i++ )
{
var model = models[i];
- var version = model.GetAnnotationValue( model ).ApiVersion;
+
+ if ( model.GetApiVersion() is not ApiVersion version )
+ {
+ continue;
+ }
if ( !mappings.TryGetValue( version, out var options ) )
{
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataOptionsPostSetup.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataOptionsPostSetup.cs
index a8e4f1ff..dfe7314f 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataOptionsPostSetup.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataOptionsPostSetup.cs
@@ -35,10 +35,7 @@ public ODataOptionsPostSetup(
///
public void PostConfigure( string? name, ODataOptions options )
{
- if ( options == null )
- {
- throw new ArgumentNullException( nameof( options ) );
- }
+ ArgumentNullException.ThrowIfNull( options );
var conventions = options.Conventions;
var replacements = 0;
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataModelBuilder.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataModelBuilder.cs
index 8f560845..9b576129 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataModelBuilder.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataModelBuilder.cs
@@ -20,12 +20,10 @@ public VersionedODataModelBuilder(
IODataApiVersionCollectionProvider apiVersionCollectionProvider,
IEnumerable modelConfigurations )
{
- if ( modelConfigurations == null )
- {
- throw new ArgumentNullException( nameof( modelConfigurations ) );
- }
+ ArgumentNullException.ThrowIfNull( apiVersionCollectionProvider );
+ ArgumentNullException.ThrowIfNull( modelConfigurations );
- this.apiVersionCollectionProvider = apiVersionCollectionProvider ?? throw new ArgumentNullException( nameof( apiVersionCollectionProvider ) );
+ this.apiVersionCollectionProvider = apiVersionCollectionProvider;
foreach ( var configuration in modelConfigurations )
{
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataOptions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataOptions.cs
index 7319d4a7..5c08429e 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataOptions.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataOptions.cs
@@ -85,12 +85,11 @@ public IReadOnlyDictionary Mapping
/// The current HTTP context .
/// The retrieved OData batch handler or null .
/// True if the was successfully retrieved; otherwise, false.
+ /// Prefer the asynchronous version of this method
+ /// .
public virtual bool TryGetBatchHandler( HttpContext context, [NotNullWhen( true )] out ODataBatchHandler? handler )
{
- if ( context == null )
- {
- throw new ArgumentNullException( nameof( context ) );
- }
+ ArgumentNullException.ThrowIfNull( context );
if ( batchMapping is null )
{
@@ -101,12 +100,33 @@ public virtual bool TryGetBatchHandler( HttpContext context, [NotNullWhen( true
return batchMapping.TryGetHandler( context, out handler );
}
+ ///
+ /// Attempts to retrieve the configured batch handler for the current context.
+ ///
+ /// The current HTTP context .
+ /// The token that can be used to cancel the operation.
+ /// A task containing the matched
+ /// or null if the no match was found.
+ public virtual ValueTask TryGetBatchHandlerAsync( HttpContext context, CancellationToken cancellationToken )
+ {
+ ArgumentNullException.ThrowIfNull( context );
+
+ if ( batchMapping is null )
+ {
+ return ValueTask.FromResult( default( ODataBatchHandler? ) );
+ }
+
+ return batchMapping.TryGetHandlerAsync( context, cancellationToken );
+ }
+
///
/// Attempts to get the current OData options.
///
/// The current HTTP context .
/// The resolved OData options or null .
/// True if the current OData were successfully resolved; otherwise, false.
+ /// Prefer the asynchronous version of this method
+ /// .
public virtual bool TryGetValue( HttpContext? context, [NotNullWhen( true )] out ODataOptions? options )
{
if ( context == null || mapping == null || mapping.Count == 0 )
@@ -132,6 +152,36 @@ public virtual bool TryGetValue( HttpContext? context, [NotNullWhen( true )] out
return mapping.TryGetValue( apiVersion, out options );
}
+ ///
+ /// Attempts to get the current OData options.
+ ///
+ /// The current HTTP context .
+ /// The token that can be used to cancel the operation.
+ /// A task containing the matched
+ /// or null if the no match was found.
+ public virtual async ValueTask TryGetValueAsync( HttpContext? context, CancellationToken cancellationToken )
+ {
+ if ( context == null || mapping == null || mapping.Count == 0 )
+ {
+ return default;
+ }
+
+ var apiVersion = context.GetRequestedApiVersion();
+
+ if ( apiVersion == null )
+ {
+ var model = new ApiVersionModel( mapping.Keys, Array.Empty() );
+ apiVersion = await ApiVersionSelector.SelectVersionAsync( context.Request, model, cancellationToken ).ConfigureAwait( false );
+
+ if ( apiVersion == null )
+ {
+ return default;
+ }
+ }
+
+ return mapping.TryGetValue( apiVersion, out var options ) ? options : default;
+ }
+
///
/// Attempts to resolve the current OData options.
///
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataTemplateTranslator.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataTemplateTranslator.cs
index 84ef5d76..93d92b9c 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataTemplateTranslator.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataTemplateTranslator.cs
@@ -6,6 +6,7 @@ namespace Asp.Versioning.OData;
using Microsoft.AspNetCore.OData.Routing.Template;
using Microsoft.OData.Edm;
using Microsoft.OData.UriParser;
+using System.Runtime.CompilerServices;
///
/// Represents a versioned OData template translator .
@@ -16,23 +17,14 @@ public sealed class VersionedODataTemplateTranslator : IODataTemplateTranslator
///
public ODataPath? Translate( ODataPathTemplate path, ODataTemplateTranslateContext context )
{
- if ( path == null )
- {
- throw new ArgumentNullException( nameof( path ) );
- }
-
- if ( context == null )
- {
- throw new ArgumentNullException( nameof( context ) );
- }
+ ArgumentNullException.ThrowIfNull( path );
+ ArgumentNullException.ThrowIfNull( context );
var apiVersion = context.HttpContext.GetRequestedApiVersion();
if ( apiVersion == null )
{
- var metadata = context.Endpoint.Metadata.GetMetadata();
-
- if ( metadata == null || !metadata.IsApiVersionNeutral )
+ if ( !IsVersionNeutral( context ) )
{
return default;
}
@@ -40,9 +32,15 @@ public sealed class VersionedODataTemplateTranslator : IODataTemplateTranslator
else
{
var model = context.Model;
- var otherApiVersion = model.GetAnnotationValue( model )?.ApiVersion;
+ var otherApiVersion = model.GetApiVersion();
- if ( !apiVersion.Equals( otherApiVersion ) )
+ // HACK: a version-neutral endpoint can fail to match here because odata tries to match the
+ // first endpoint metadata when there could be multiple. such an endpoint is expected to be
+ // the same in all versions so allow it to flow through. revisit if/when odata fixes this.
+ //
+ // REF: https://github.com/OData/AspNetCoreOData/issues/753
+ // REF: https://github.com/OData/AspNetCoreOData/blob/main/src/Microsoft.AspNetCore.OData/Routing/ODataRoutingMatcherPolicy.cs#L86
+ if ( !apiVersion.Equals( otherApiVersion ) && !IsVersionNeutral( context ) )
{
return default;
}
@@ -58,4 +56,9 @@ public sealed class VersionedODataTemplateTranslator : IODataTemplateTranslator
return new( context.Segments );
}
+
+ [MethodImpl( MethodImplOptions.AggressiveInlining )]
+ private static bool IsVersionNeutral( ODataTemplateTranslateContext context ) =>
+ context.Endpoint.Metadata.GetMetadata() is ApiVersionMetadata metadata
+ && metadata.IsApiVersionNeutral;
}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt b/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt
index 5f282702..b5318606 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt
@@ -1 +1 @@
-
\ No newline at end of file
+Support OData 9.0 ([#1103](https://github.com/dotnet/aspnet-api-versioning/issues/1103))
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/DefaultMetadataMatcherPolicy.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/DefaultMetadataMatcherPolicy.cs
index bc58fc89..a4b7bfd3 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/DefaultMetadataMatcherPolicy.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/DefaultMetadataMatcherPolicy.cs
@@ -3,6 +3,7 @@
namespace Asp.Versioning.Routing;
using Asp.Versioning;
+using Asp.Versioning.ApiExplorer;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.OData.Routing;
using Microsoft.AspNetCore.OData.Routing.Template;
@@ -35,11 +36,7 @@ public DefaultMetadataMatcherPolicy(
IApiVersionParameterSource parameterSource,
IOptions options )
{
- if ( parameterSource == null )
- {
- throw new ArgumentNullException( nameof( parameterSource ) );
- }
-
+ ArgumentNullException.ThrowIfNull( parameterSource );
versionsByUrl = parameterSource.VersionsByUrl();
this.options = options;
}
@@ -50,10 +47,7 @@ public DefaultMetadataMatcherPolicy(
///
public virtual bool AppliesToEndpoints( IReadOnlyList endpoints )
{
- if ( endpoints == null )
- {
- throw new ArgumentNullException( nameof( endpoints ) );
- }
+ ArgumentNullException.ThrowIfNull( endpoints );
for ( var i = 0; i < endpoints.Count; i++ )
{
@@ -69,10 +63,7 @@ public virtual bool AppliesToEndpoints( IReadOnlyList endpoints )
///
public IReadOnlyList GetEdges( IReadOnlyList endpoints )
{
- if ( endpoints == null )
- {
- throw new ArgumentNullException( nameof( endpoints ) );
- }
+ ArgumentNullException.ThrowIfNull( endpoints );
var edges = default( List );
var lowestApiVersion = default( ApiVersion );
@@ -88,7 +79,7 @@ public IReadOnlyList GetEdges( IReadOnlyList endpoints
continue;
}
- edges ??= new();
+ edges ??= [];
edges.Add( endpoint );
var model = endpoint.Metadata.GetMetadata()!.Map( Explicit | Implicit );
@@ -130,17 +121,14 @@ public IReadOnlyList GetEdges( IReadOnlyList endpoints
return Array.Empty();
}
- var state = (lowestApiVersion, routePatterns?.ToArray() ?? Array.Empty());
+ var state = (lowestApiVersion, routePatterns?.ToArray() ?? []);
return new PolicyNodeEdge[] { new( state, edges ) };
}
///
public PolicyJumpTable BuildJumpTable( int exitDestination, IReadOnlyList edges )
{
- if ( edges == null )
- {
- throw new ArgumentNullException( nameof( edges ) );
- }
+ ArgumentNullException.ThrowIfNull( edges );
Debug.Assert( edges.Count == 1, $"Only a single edge was expected, but {edges.Count} edges were provided" );
@@ -159,6 +147,7 @@ public PolicyJumpTable BuildJumpTable( int exitDestination, IReadOnlyList
new ApiVersionMatcherPolicy(
ApiVersionParser.Default,
+ Enumerable.Empty(),
Options.Create( new ApiVersioningOptions() ),
new NullLogger() ).Order;
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedAttributeRoutingConvention.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedAttributeRoutingConvention.cs
index 5dd11990..230017ed 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedAttributeRoutingConvention.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedAttributeRoutingConvention.cs
@@ -3,7 +3,6 @@
namespace Asp.Versioning.Routing;
using Asp.Versioning.ApplicationModels;
-using Asp.Versioning.OData;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.OData.Routing.Conventions;
using Microsoft.AspNetCore.OData.Routing.Parser;
@@ -29,10 +28,7 @@ public VersionedAttributeRoutingConvention(
///
public override bool AppliesToAction( ODataControllerActionContext context )
{
- if ( context == null )
- {
- throw new ArgumentNullException( nameof( context ) );
- }
+ ArgumentNullException.ThrowIfNull( context );
var metadata = context.Action
.Selectors
@@ -55,7 +51,7 @@ public override bool AppliesToAction( ODataControllerActionContext context )
return false;
}
- var apiVersion = edm.GetAnnotationValue( edm )?.ApiVersion;
+ var apiVersion = edm.GetApiVersion();
if ( apiVersion == null || !metadata.IsMappedTo( apiVersion ) )
{
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedMetadataRoutingConvention.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedMetadataRoutingConvention.cs
index 77ff8f71..c506e50f 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedMetadataRoutingConvention.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedMetadataRoutingConvention.cs
@@ -19,11 +19,7 @@ public class VersionedMetadataRoutingConvention : MetadataRoutingConvention
///
public override bool AppliesToController( ODataControllerActionContext context )
{
- if ( context == null )
- {
- throw new ArgumentNullException( nameof( context ) );
- }
-
+ ArgumentNullException.ThrowIfNull( context );
metadataController ??= typeof( VersionedMetadataController );
return metadataController.IsAssignableFrom( context.Controller.ControllerType );
}
@@ -31,10 +27,7 @@ public override bool AppliesToController( ODataControllerActionContext context )
///
public override bool AppliesToAction( ODataControllerActionContext context )
{
- if ( context == null )
- {
- throw new ArgumentNullException( nameof( context ) );
- }
+ ArgumentNullException.ThrowIfNull( context );
var action = context.Action;
var actionName = action.ActionMethod.Name;
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/ApiExplorer/ODataApiDescriptionProviderTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/ApiExplorer/ODataApiDescriptionProviderTest.cs
index 3c3f3136..532abb00 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/ApiExplorer/ODataApiDescriptionProviderTest.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/ApiExplorer/ODataApiDescriptionProviderTest.cs
@@ -9,6 +9,7 @@ namespace Asp.Versioning.ApiExplorer;
using Microsoft.AspNetCore.OData;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
+using System.Buffers;
public class ODataApiDescriptionProviderTest
{
@@ -152,6 +153,8 @@ private void AssertVersion1( ApiDescriptionGroup group )
new { HttpMethod = "GET", GroupName, RelativePath = "api/People/{key}" },
},
options => options.ExcludingMissingMembers() );
+
+ AssertQueryOptionWithoutOData( items[0], "filter", "author", "published" );
}
private void AssertVersion2( ApiDescriptionGroup group )
@@ -229,9 +232,33 @@ private void AssertVersion3( ApiDescriptionGroup group )
items.Should().BeEquivalentTo( expected, options => options.ExcludingMissingMembers() );
}
- private void PrintGroup( IReadOnlyList items )
+ private static void AssertQueryOptionWithoutOData( ApiDescription description, string name, string property, params string[] otherProperties )
+ {
+ var parameter = description.ParameterDescriptions.Single( p => p.Name == name );
+ var count = otherProperties.Length + 1;
+ string suffix;
+
+ if ( count == 1 )
+ {
+ suffix = property;
+ }
+ else
+ {
+ var pool = ArrayPool.Shared;
+ var properties = pool.Rent( count );
+
+ properties[0] = property;
+ Array.Copy( otherProperties, 0, properties, 1, count - 1 );
+
+ suffix = string.Join( ", ", properties, 0, count );
+ }
+
+ parameter.ModelMetadata.Description.Should().EndWith( suffix + '.' );
+ }
+
+ private void PrintGroup( ApiDescription[] items )
{
- for ( var i = 0; i < items.Count; i++ )
+ for ( var i = 0; i < items.Length; i++ )
{
var item = items[i];
console.WriteLine( $"[{item.GroupName}] {item.HttpMethod} {item.RelativePath}" );
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Asp.Versioning.OData.ApiExplorer.Tests.csproj b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Asp.Versioning.OData.ApiExplorer.Tests.csproj
index afcd5741..00feabbc 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Asp.Versioning.OData.ApiExplorer.Tests.csproj
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Asp.Versioning.OData.ApiExplorer.Tests.csproj
@@ -1,7 +1,7 @@
- net7.0
+ $(DefaultTargetFramework)
Asp.Versioning
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs
index e5cd95ce..fd3717ba 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs
@@ -1,5 +1,10 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+//// Ignore Spelling: Dlike
+//// Ignore Spelling: Multipart
+//// Ignore Spelling: nonaction
+//// Ignore Spelling: nonquery
+
namespace Asp.Versioning.Conventions;
using Asp.Versioning.OData;
@@ -19,6 +24,7 @@ namespace Asp.Versioning.Conventions;
using Microsoft.OData.ModelBuilder;
using Microsoft.OData.ModelBuilder.Config;
using System.Reflection;
+using Xunit;
using static Microsoft.AspNetCore.Http.StatusCodes;
using static Microsoft.AspNetCore.Mvc.ModelBinding.BindingSource;
using static Microsoft.AspNetCore.OData.Query.AllowedArithmeticOperators;
@@ -469,6 +475,50 @@ public void apply_to_should_use_model_bound_query_attributes()
options => options.ExcludingMissingMembers() );
}
+ [Fact]
+ public void apply_should_override_model_bound_settings_with_enable_query_attribute()
+ {
+ // arrange
+ var builder = new ODataConventionModelBuilder().EnableLowerCamelCase();
+
+ builder.EntitySet( "Customers" );
+
+ var validationSettings = new ODataValidationSettings()
+ {
+ AllowedQueryOptions = AllowedQueryOptions.None,
+ AllowedArithmeticOperators = AllowedArithmeticOperators.None,
+ AllowedLogicalOperators = AllowedLogicalOperators.None,
+ AllowedFunctions = AllowedFunctions.None,
+ };
+ var settings = new TestODataQueryOptionSettings( typeof( Customer ) );
+ var convention = new ODataValidationSettingsConvention( validationSettings, settings );
+ var model = builder.GetEdmModel();
+ var description = NewApiDescription( typeof( CustomersController ), typeof( IEnumerable ), model );
+
+ // act
+ convention.ApplyTo( description );
+
+ // assert
+ var parameter = description.ParameterDescriptions.Single();
+
+ parameter.Should().BeEquivalentTo(
+ new
+ {
+ Name = "$filter",
+ Source = Query,
+ Type = typeof( string ),
+ DefaultValue = default( object ),
+ IsRequired = false,
+ ModelMetadata = new { Description = "Test" },
+ ParameterDescriptor = new
+ {
+ Name = "$filter",
+ ParameterType = typeof( string ),
+ },
+ },
+ options => options.ExcludingMissingMembers() );
+ }
+
[Fact]
public void apply_to_should_process_odataX2Dlike_api_description()
{
@@ -589,7 +639,7 @@ public static IEnumerable EnableQueryAttributeData
MethodInfo = typeof( ControllerBase ).GetRuntimeMethod( nameof( ControllerBase.Ok ), Type.EmptyTypes ),
EndpointMetadata = new object[]
{
- new ODataRoutingMetadata( string.Empty, model, new() ),
+ new ODataRoutingMetadata( string.Empty, model, [] ),
},
},
HttpMethod = method,
@@ -620,7 +670,7 @@ private static ApiDescription NewApiDescription( Type controllerType, Type respo
MethodInfo = controllerType.GetRuntimeMethods().Single( m => m.Name == "Get" ),
EndpointMetadata = new object[]
{
- new ODataRoutingMetadata( string.Empty, model, new() ),
+ new ODataRoutingMetadata( string.Empty, model, [] ),
},
},
HttpMethod = "GET",
@@ -678,6 +728,13 @@ public class OrdersController : ODataController
public IActionResult Get( ODataQueryOptions options ) => Ok();
}
+ public class CustomersController : ODataController
+ {
+ [EnableQuery( AllowedQueryOptions = Filter )]
+ [ProducesResponseType( typeof( IEnumerable ), Status200OK )]
+ public IActionResult Get( ODataQueryOptions options ) => Ok();
+ }
+
[Select]
[Filter]
[Count]
@@ -693,6 +750,12 @@ public class Order
public int Quantity { get; set; }
}
+ [Page( MaxTop = 25, PageSize = 25 )]
+ public class Customer
+ {
+ public int CustomerId { get; set; }
+ }
+
private sealed class TestODataQueryOptionSettings : ODataQueryOptionSettings
{
internal TestODataQueryOptionSettings( Type type, bool dollarPrefix = true ) :
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs
index d29fe190..a70422b7 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-
namespace Asp.Versioning.Simulators.Configuration;
using Asp.Versioning.OData;
@@ -15,6 +13,8 @@ public class AllConfigurations : IModelConfiguration
///
public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix )
{
+ ArgumentNullException.ThrowIfNull( builder );
+
builder.Function( "GetSalesTaxRate" ).Returns().Parameter( "PostalCode" );
}
}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs
index bfbe387d..573b67a0 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-
namespace Asp.Versioning.Simulators.Configuration;
using Asp.Versioning.OData;
@@ -16,6 +14,8 @@ public class OrderModelConfiguration : IModelConfiguration
///
public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix )
{
+ ArgumentNullException.ThrowIfNull( builder );
+
var order = builder.EntitySet( "Orders" ).EntityType.HasKey( o => o.Id );
if ( apiVersion < ApiVersions.V2 )
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs
index bd869bff..662bf0ba 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-
namespace Asp.Versioning.Simulators.Configuration;
using Asp.Versioning.OData;
@@ -16,6 +14,8 @@ public class PersonModelConfiguration : IModelConfiguration
///
public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix )
{
+ ArgumentNullException.ThrowIfNull( builder );
+
var person = builder.EntitySet( "People" ).EntityType.HasKey( p => p.Id );
if ( apiVersion < ApiVersions.V3 )
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs
index b210dbdb..eb71daf6 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-
namespace Asp.Versioning.Simulators.Configuration;
using Asp.Versioning.OData;
@@ -16,6 +14,8 @@ public class ProductConfiguration : IModelConfiguration
///
public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix )
{
+ ArgumentNullException.ThrowIfNull( builder );
+
if ( apiVersion < ApiVersions.V3 )
{
return;
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs
index 7ce101b7..9bcd5bbc 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA1062 // Validate arguments of public methods
-
namespace Asp.Versioning.Simulators.Configuration;
using Asp.Versioning.OData;
@@ -16,6 +14,8 @@ public class SupplierConfiguration : IModelConfiguration
///
public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix )
{
+ ArgumentNullException.ThrowIfNull( builder );
+
if ( apiVersion < ApiVersions.V3 )
{
return;
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Book.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Book.cs
index 00fec7f1..97610da6 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Book.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Book.cs
@@ -2,9 +2,12 @@
namespace Asp.Versioning.Simulators.Models;
+using Microsoft.OData.ModelBuilder;
+
///
/// Represents a book.
///
+[Filter( "author", "published" )]
public class Book
{
///
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs
index 83a08d66..7b503c6b 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs
@@ -1,7 +1,5 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
-#pragma warning disable CA2227 // Collection properties should be read only
-
namespace Asp.Versioning.Simulators.Models;
public class Supplier
@@ -10,5 +8,7 @@ public class Supplier
public string Name { get; set; }
+#pragma warning disable CA2227 // Collection properties should be read only
public ICollection Products { get; set; }
+#pragma warning restore CA2227 // Collection properties should be read only
}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs
index 438a0099..d6b7e96a 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs
@@ -16,15 +16,15 @@ namespace Asp.Versioning.Simulators.V1;
[Route( "api/[controller]" )]
public class BooksController : ControllerBase
{
- private static readonly Book[] books = new Book[]
- {
+ private static readonly Book[] books =
+ [
new() { Id = "9781847490599", Title = "Anna Karenina", Author = "Leo Tolstoy", Published = 1878 },
new() { Id = "9780198800545", Title = "War and Peace", Author = "Leo Tolstoy", Published = 1869 },
new() { Id = "9780684801520", Title = "The Great Gatsby", Author = "F. Scott Fitzgerald", Published = 1925 },
new() { Id = "9780486280615", Title = "The Adventures of Huckleberry Finn", Author = "Mark Twain", Published = 1884 },
new() { Id = "9780140430820", Title = "Moby Dick", Author = "Herman Melville", Published = 1851 },
new() { Id = "9780060934347", Title = "Don Quixote", Author = "Miguel de Cervantes", Published = 1605 },
- };
+ ];
///
/// Gets all books.
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/ApplicationModels/ODataControllerSpecificationTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/ApplicationModels/ODataControllerSpecificationTest.cs
index 7855beca..889d3b34 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/ApplicationModels/ODataControllerSpecificationTest.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/ApplicationModels/ODataControllerSpecificationTest.cs
@@ -29,25 +29,26 @@ public void is_satisfied_by_should_return_expected_value( Type controllerType, b
result.Should().Be( expected );
}
+#pragma warning disable IDE0079
#pragma warning disable CA1812
private sealed class NormalODataController : ODataController
{
[EnableQuery]
- public IActionResult Get() => Ok();
+ public OkResult Get() => Ok();
}
[ODataAttributeRouting]
private sealed class CustomODataController : ControllerBase
{
[EnableQuery]
- public IActionResult Get() => Ok();
+ public OkResult Get() => Ok();
}
[Route( "api/test" )]
private sealed class NonODataController : ControllerBase
{
[HttpGet]
- public IActionResult Get() => Ok();
+ public OkResult Get() => Ok();
}
}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Asp.Versioning.OData.Tests.csproj b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Asp.Versioning.OData.Tests.csproj
index e72dee9a..37771eff 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Asp.Versioning.OData.Tests.csproj
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Asp.Versioning.OData.Tests.csproj
@@ -1,7 +1,7 @@
- net7.0
+ $(DefaultTargetFramework)
Asp.Versioning
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Controllers/VersionedMetadataControllerTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Controllers/VersionedMetadataControllerTest.cs
index 516ef02c..e363fa4c 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Controllers/VersionedMetadataControllerTest.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Controllers/VersionedMetadataControllerTest.cs
@@ -34,6 +34,7 @@ public async Task options_should_return_expected_headers()
response.Content.Headers.Allow.Should().BeEquivalentTo( "GET", "OPTIONS" );
}
+#pragma warning disable IDE0079
#pragma warning disable CA1812
#pragma warning disable CA1822 // Mark members as static
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs
index e344a34d..0987219f 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs
@@ -28,10 +28,11 @@ public void populate_feature_should_discover_valid_model_configurations()
provider.PopulateFeature( partManager.ApplicationParts, feature );
// assert
- feature.ModelConfigurations.Should().Equal( new[] { typeof( PublicModelConfiguration ) } );
+ feature.ModelConfigurations.Should().Equal( [typeof( PublicModelConfiguration )] );
}
}
+#pragma warning disable IDE0079
#pragma warning disable CA1812
#pragma warning disable SA1402 // File may only contain a single type
#pragma warning disable SA1403 // File may only contain a single namespace
@@ -40,10 +41,10 @@ namespace ModelConfigurations
{
internal struct ValueTypeModelConfiguration : IModelConfiguration
{
- public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { }
+ public readonly void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { }
}
- internal class PrivateModelConfiguration : IModelConfiguration
+ internal sealed class PrivateModelConfiguration : IModelConfiguration
{
public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { }
}
@@ -53,12 +54,12 @@ public abstract class AbstractModelConfiguration : IModelConfiguration
public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { }
}
- public class GenericModelConfiguration : IModelConfiguration
+ public sealed class GenericModelConfiguration : IModelConfiguration
{
public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { }
}
- public class PublicModelConfiguration : IModelConfiguration
+ public sealed class PublicModelConfiguration : IModelConfiguration
{
public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { }
}
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/VersionedODataModelBuilderTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/VersionedODataModelBuilderTest.cs
index 29be43a2..b40917a9 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/VersionedODataModelBuilderTest.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/VersionedODataModelBuilderTest.cs
@@ -29,7 +29,7 @@ public void get_edm_models_should_return_expected_results()
var model = builder.GetEdmModels().Single();
// assert
- model.GetAnnotationValue( model ).ApiVersion.Should().Be( apiVersion );
+ model.GetApiVersion().Should().Be( apiVersion );
modelCreated.Verify( f => f( It.IsAny(), model ), Once() );
}
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/DefaultMetadataMatcherPolicyTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/DefaultMetadataMatcherPolicyTest.cs
index 2e3273c3..0d2bd095 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/DefaultMetadataMatcherPolicyTest.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/DefaultMetadataMatcherPolicyTest.cs
@@ -17,7 +17,7 @@ public void applies_to_endpoints_should_return_true_for_service_document()
var paramSource = Mock.Of();
var options = Options.Create( new ApiVersioningOptions() );
var policy = new DefaultMetadataMatcherPolicy( paramSource, options );
- var metadata = new ODataRoutingMetadata( string.Empty, EdmCoreModel.Instance, new ODataPathTemplate() );
+ var metadata = new ODataRoutingMetadata( string.Empty, EdmCoreModel.Instance, [] );
var items = new object[] { metadata };
var endpoints = new Endpoint[] { new( Limbo, new( items ), default ) };
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs
index 8f39cd8d..9181574e 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
+//// Ignore Spelling: Dneutral
+
namespace Asp.Versioning.Routing;
using Asp.Versioning.OData;
diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedMetadataRoutingConventionTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedMetadataRoutingConventionTest.cs
index 8559374e..0346cac2 100644
--- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedMetadataRoutingConventionTest.cs
+++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedMetadataRoutingConventionTest.cs
@@ -52,6 +52,7 @@ public void applied_to_action_should_return_true()
action.Selectors.Should().HaveCount( 1 );
}
+#pragma warning disable IDE0079
#pragma warning disable CA1812
private sealed class AnotherVersionedMetadataController : VersionedMetadataController
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationCollection.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationCollection.cs
new file mode 100644
index 00000000..85427ba8
--- /dev/null
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationCollection.cs
@@ -0,0 +1,137 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.ApiExplorer;
+
+using System.Collections;
+
+///
+/// Represents a collection of collated API version metadata.
+///
+public class ApiVersionMetadataCollationCollection : IList, IReadOnlyList
+{
+ private readonly List items;
+ private readonly List groups;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ApiVersionMetadataCollationCollection()
+ {
+ items = [];
+ groups = [];
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The initial capacity of the collection.
+ public ApiVersionMetadataCollationCollection( int capacity )
+ {
+ items = new( capacity );
+ groups = new( capacity );
+ }
+
+ ///
+ /// Gets the item in the list at the specified index.
+ ///
+ /// The zero-based index of the item to retrieve.
+ /// The item at the specified index.
+ public ApiVersionMetadata this[int index] => items[index];
+
+ ApiVersionMetadata IList.this[int index]
+ {
+ get => items[index];
+ set => throw new NotSupportedException();
+ }
+
+ ///
+ public int Count => items.Count;
+
+#pragma warning disable IDE0079
+#pragma warning disable CA1033 // Interface methods should be callable by child types
+ bool ICollection.IsReadOnly => ( (ICollection) items ).IsReadOnly;
+#pragma warning restore CA1033 // Interface methods should be callable by child types
+#pragma warning restore IDE0079
+
+ ///
+ public void Add( ApiVersionMetadata item ) => Insert( Count, item, default );
+
+ ///
+ /// Adds an item to the collection.
+ ///
+ /// The item to add.
+ /// The associated group name, if any.
+ public void Add( ApiVersionMetadata item, string? groupName ) => Insert( Count, item, groupName );
+
+ ///
+ public void Clear()
+ {
+ items.Clear();
+ groups.Clear();
+ }
+
+ ///
+ public bool Contains( ApiVersionMetadata item ) => item != null && items.Contains( item );
+
+ ///
+ public void CopyTo( ApiVersionMetadata[] array, int arrayIndex ) => items.CopyTo( array, arrayIndex );
+
+ ///
+ public IEnumerator GetEnumerator() => items.GetEnumerator();
+
+ ///
+ public int IndexOf( ApiVersionMetadata item ) => item == null ? -1 : items.IndexOf( item );
+
+ ///
+ public void Insert( int index, ApiVersionMetadata item ) => Insert( index, item, default );
+
+ ///
+ /// Inserts an item into the collection.
+ ///
+ /// The zero-based index where insertion takes place.
+ /// The item to insert.
+ /// The associated group name, if any.
+ public void Insert( int index, ApiVersionMetadata item, string? groupName )
+ {
+ items.Insert( index, item ?? throw new ArgumentNullException( nameof( item ) ) );
+ groups.Insert( index, groupName );
+ }
+
+ ///
+ public bool Remove( ApiVersionMetadata item )
+ {
+ if ( item == null )
+ {
+ return false;
+ }
+
+ var index = items.IndexOf( item );
+
+ if ( index < 0 )
+ {
+ return false;
+ }
+
+ RemoveAt( index );
+ return true;
+ }
+
+ ///
+ public void RemoveAt( int index )
+ {
+ items.RemoveAt( index );
+ groups.RemoveAt( index );
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() => ( (IEnumerable) items ).GetEnumerator();
+
+ ///
+ /// Gets the group name for the item at the specified index.
+ ///
+ /// The zero-based index of the item to get the group name for.
+ /// The associated group name or null .
+ /// If the specified is out of range, null
+ /// is returned.
+ public string? GroupName( int index ) =>
+ index < 0 || index >= groups.Count ? default : groups[index];
+}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationContext.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationContext.cs
new file mode 100644
index 00000000..313c2f26
--- /dev/null
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationContext.cs
@@ -0,0 +1,15 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.ApiExplorer;
+
+///
+/// Represents the context used during API version metadata collation.
+///
+public class ApiVersionMetadataCollationContext
+{
+ ///
+ /// Gets the read-only list of collation results.
+ ///
+ /// The read-only list of collation results.
+ public ApiVersionMetadataCollationCollection Results { get; } = [];
+}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/DefaultEndpointInspector.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/DefaultEndpointInspector.cs
new file mode 100644
index 00000000..7109d87f
--- /dev/null
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/DefaultEndpointInspector.cs
@@ -0,0 +1,15 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.ApiExplorer;
+
+using Microsoft.AspNetCore.Http;
+
+///
+/// Represents the default endpoint inspector .
+///
+[CLSCompliant(false)]
+public sealed class DefaultEndpointInspector : IEndpointInspector
+{
+ ///
+ public bool IsControllerAction( Endpoint endpoint ) => false;
+}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/EndpointApiVersionMetadataCollationProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/EndpointApiVersionMetadataCollationProvider.cs
new file mode 100644
index 00000000..e5ce20fb
--- /dev/null
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/EndpointApiVersionMetadataCollationProvider.cs
@@ -0,0 +1,65 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.ApiExplorer;
+
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.Primitives;
+
+///
+/// Represents the API version metadata collection provider for endpoints.
+///
+[CLSCompliant( false )]
+public sealed class EndpointApiVersionMetadataCollationProvider : IApiVersionMetadataCollationProvider
+{
+ private readonly EndpointDataSource endpointDataSource;
+ private readonly IEndpointInspector endpointInspector;
+ private int version;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The underlying endpoint data source .
+ [Obsolete( "Use the constructor that accepts IEndpointInspector. This constructor will be removed in a future version." )]
+ public EndpointApiVersionMetadataCollationProvider( EndpointDataSource endpointDataSource )
+ : this( endpointDataSource, new DefaultEndpointInspector() ) { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The underlying endpoint data source .
+ /// The endpoint inspector used to inspect endpoints.
+ public EndpointApiVersionMetadataCollationProvider( EndpointDataSource endpointDataSource, IEndpointInspector endpointInspector )
+ {
+ ArgumentNullException.ThrowIfNull( endpointDataSource );
+ ArgumentNullException.ThrowIfNull( endpointInspector );
+
+ this.endpointDataSource = endpointDataSource;
+ this.endpointInspector = endpointInspector;
+ ChangeToken.OnChange( endpointDataSource.GetChangeToken, () => ++version );
+ }
+
+ ///
+ public int Version => version;
+
+ ///
+ public void Execute( ApiVersionMetadataCollationContext context )
+ {
+ ArgumentNullException.ThrowIfNull( context );
+
+ var endpoints = endpointDataSource.Endpoints;
+
+ for ( var i = 0; i < endpoints.Count; i++ )
+ {
+ var endpoint = endpoints[i];
+
+ if ( endpoint.Metadata.GetMetadata() is not ApiVersionMetadata item ||
+ endpointInspector.IsControllerAction( endpoint ) )
+ {
+ continue;
+ }
+
+ var groupName = endpoint.Metadata.OfType().LastOrDefault()?.EndpointGroupName;
+ context.Results.Add( item, groupName );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IApiVersionDescriptionProviderFactoryExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IApiVersionDescriptionProviderFactoryExtensions.cs
new file mode 100644
index 00000000..4a20e9c1
--- /dev/null
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IApiVersionDescriptionProviderFactoryExtensions.cs
@@ -0,0 +1,34 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.ApiExplorer;
+
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.Primitives;
+
+///
+/// Provides extension methods for .
+///
+[CLSCompliant( false )]
+public static class IApiVersionDescriptionProviderFactoryExtensions
+{
+ ///
+ /// Creates and returns an API version description provider.
+ ///
+ /// The extended .
+ /// A new API version description provider .
+ public static IApiVersionDescriptionProvider Create( this IApiVersionDescriptionProviderFactory factory )
+ {
+ ArgumentNullException.ThrowIfNull( factory );
+ return factory.Create( new EmptyEndpointDataSource() );
+ }
+
+ private sealed class EmptyEndpointDataSource : EndpointDataSource
+ {
+ public override IReadOnlyList Endpoints { get; } = [];
+
+ public override IChangeToken GetChangeToken() => new CancellationChangeToken( CancellationToken.None );
+
+ public override IReadOnlyList GetGroupedEndpoints( RouteGroupContext context ) => Endpoints;
+ }
+}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IApiVersionMetadataCollationProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IApiVersionMetadataCollationProvider.cs
new file mode 100644
index 00000000..04a386a6
--- /dev/null
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IApiVersionMetadataCollationProvider.cs
@@ -0,0 +1,21 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.ApiExplorer;
+
+///
+/// Defines the behavior of an API version metadata collation provider.
+///
+public interface IApiVersionMetadataCollationProvider
+{
+ ///
+ /// Gets version of the underlying provider results.
+ ///
+ /// The version of the provider results. This can be used to detect changes.
+ int Version { get; }
+
+ ///
+ /// Executes the provider using the given context.
+ ///
+ /// The collation context.
+ void Execute( ApiVersionMetadataCollationContext context );
+}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IEndpointInspector.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IEndpointInspector.cs
new file mode 100644
index 00000000..900edf94
--- /dev/null
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IEndpointInspector.cs
@@ -0,0 +1,19 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.ApiExplorer;
+
+using Microsoft.AspNetCore.Http;
+
+///
+/// Defines the behavior of an endpoint inspector.
+///
+[CLSCompliant( false )]
+public interface IEndpointInspector
+{
+ ///
+ /// Determines whether the specified endpoint is a controller action.
+ ///
+ /// The endpoint to inspect.
+ /// True if the is for a controller action; otherwise, false.
+ bool IsControllerAction( Endpoint endpoint );
+}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs
index 5c515096..8437429a 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs
@@ -5,6 +5,7 @@ namespace Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System.Globalization;
+using System.Runtime.CompilerServices;
///
/// Represents the API versioning feature.
@@ -33,12 +34,7 @@ public IReadOnlyList RawRequestedApiVersions
{
if ( rawApiVersions is null )
{
- var reader =
- context.RequestServices.GetService() ??
- ApiVersionReader.Combine(
- new QueryStringApiVersionReader(),
- new UrlSegmentApiVersionReader() );
-
+ var reader = context.RequestServices.GetService() ?? ApiVersionReader.Default;
rawApiVersions = reader.Read( context.Request );
}
@@ -58,16 +54,16 @@ public string? RawRequestedApiVersion
{
0 => default,
1 => values[0],
-#pragma warning disable CA1065 // Do not raise exceptions in unexpected locations; existing behavior via IApiVersionReader.Read
- _ => throw new AmbiguousApiVersionException(
- string.Format( CultureInfo.CurrentCulture, CommonSR.MultipleDifferentApiVersionsRequested, string.Join( ", ", values ) ),
- values ),
-#pragma warning restore CA1065
+#pragma warning disable IDE0079
+#pragma warning disable CA1065 // Do not raise exceptions in unexpected locations
+ _ => throw NewAmbiguousApiVersionException( values ),
+#pragma warning restore CA1065 // Do not raise exceptions in unexpected locations
+#pragma warning restore IDE0079
};
}
set
{
- rawApiVersions = string.IsNullOrEmpty( value ) ? default : new[] { value };
+ rawApiVersions = string.IsNullOrEmpty( value ) ? default : [value];
}
}
@@ -88,7 +84,8 @@ public ApiVersion? RequestedApiVersion
return apiVersion;
}
- var parser = context.RequestServices.GetRequiredService();
+ var parser = context.RequestServices.GetService()
+ ?? ApiVersionParser.Default;
try
{
@@ -105,10 +102,20 @@ public ApiVersion? RequestedApiVersion
{
apiVersion = value;
- if ( apiVersion is not null && ( rawApiVersions is null || rawApiVersions.Count == 0 ) )
+ if ( apiVersion is not null &&
+ ( rawApiVersions is null || rawApiVersions.Count == 0 ) )
{
- rawApiVersions = new[] { apiVersion.ToString() };
+ rawApiVersions = [apiVersion.ToString()];
}
}
}
+
+ [MethodImpl( MethodImplOptions.AggressiveInlining )]
+ private static AmbiguousApiVersionException NewAmbiguousApiVersionException( IReadOnlyList values ) =>
+ new(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ Format.MultipleDifferentApiVersionsRequested,
+ string.Join( ", ", [.. values], 0, values.Count ) ),
+ values );
}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj
index 4dc9d40d..3abd2a2c 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj
@@ -1,13 +1,14 @@
- 7.0.0
- 7.0.0.0
- net7.0
+ 8.1.0
+ 8.1.0.0
+ $(DefaultTargetFramework)
Asp.Versioning
ASP.NET Core API Versioning
A service API versioning library for Microsoft ASP.NET Core.
Asp;AspNet;AspNetCore;Versioning
+ true
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/ApiVersionSetBuilder.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/ApiVersionSetBuilder.cs
index 3a0303f6..e24853fc 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/ApiVersionSetBuilder.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/ApiVersionSetBuilder.cs
@@ -110,10 +110,7 @@ public virtual ApiVersionSetBuilder AdvertisesDeprecatedApiVersion( ApiVersion a
/// A new API version model .
protected internal virtual ApiVersionModel BuildApiVersionModel( ApiVersioningOptions options )
{
- if ( options == null )
- {
- throw new ArgumentNullException( nameof( options ) );
- }
+ ArgumentNullException.ThrowIfNull( options );
if ( VersionNeutral )
{
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/ApiVersionSetBuilderFactory.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/ApiVersionSetBuilderFactory.cs
new file mode 100644
index 00000000..7d5960dd
--- /dev/null
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/ApiVersionSetBuilderFactory.cs
@@ -0,0 +1,10 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.Builder;
+
+///
+/// Creates and returns a new API version set builder.
+///
+/// The name of the API associated with the builder, if any.
+/// A new API version set builder .
+public delegate ApiVersionSetBuilder ApiVersionSetBuilderFactory( string? name = default );
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/DefaultApiVersionSetBuilderFactory.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/DefaultApiVersionSetBuilderFactory.cs
deleted file mode 100644
index c8f879a1..00000000
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/DefaultApiVersionSetBuilderFactory.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-
-namespace Asp.Versioning.Builder;
-
-///
-/// Represents the default API version set builder factory.
-///
-public class DefaultApiVersionSetBuilderFactory : IApiVersionSetBuilderFactory
-{
- ///
- public ApiVersionSetBuilder Create( string? name = default ) => CreateInstance( name );
-
- ///
- /// Creates and returns a new builder instance.
- ///
- /// The optional name associated with the builder.
- /// A new API version set builder .
- protected virtual ApiVersionSetBuilder CreateInstance( string? name ) => new( name );
-}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs
new file mode 100644
index 00000000..250083cd
--- /dev/null
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs
@@ -0,0 +1,287 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.Builder;
+
+using Asp.Versioning;
+using Asp.Versioning.Routing;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using static Asp.Versioning.ApiVersionParameterLocation;
+using static Asp.Versioning.ApiVersionProviderOptions;
+
+internal static class EndpointBuilderFinalizer
+{
+ internal static void FinalizeEndpoints( EndpointBuilder endpointBuilder )
+ {
+ var versionSet = GetApiVersionSet( endpointBuilder.Metadata );
+ Finialize( endpointBuilder, versionSet );
+ }
+
+ internal static void FinalizeRoutes( EndpointBuilder endpointBuilder )
+ {
+ var versionSet = endpointBuilder.ApplicationServices.GetService();
+ Finialize( endpointBuilder, versionSet );
+ }
+
+ private static void Finialize( EndpointBuilder endpointBuilder, ApiVersionSet? versionSet )
+ {
+ if ( versionSet is null )
+ {
+ // this could only happen if the ApiVersionSet was removed elsewhere from the metadata
+ endpointBuilder.Metadata.Add( ApiVersionMetadata.Empty );
+ return;
+ }
+
+ var services = endpointBuilder.ApplicationServices;
+ var endpointMetadata = endpointBuilder.Metadata;
+ var options = services.GetRequiredService>().Value;
+ var metadata = Build( endpointMetadata, versionSet, options );
+ var reportApiVersions = ReportApiVersions( endpointMetadata ) ||
+ options.ReportApiVersions ||
+ versionSet.ReportApiVersions;
+
+ endpointBuilder.Metadata.Add( metadata );
+
+ var requestDelegate = default( RequestDelegate );
+
+ if ( reportApiVersions )
+ {
+ requestDelegate = EnsureRequestDelegate( requestDelegate, endpointBuilder.RequestDelegate );
+
+ var reporter = services.GetRequiredService();
+
+ requestDelegate = new ReportApiVersionsDecorator( requestDelegate, reporter, metadata );
+ endpointBuilder.RequestDelegate = requestDelegate;
+ }
+
+ var parameterSource = services.GetRequiredService();
+
+ if ( parameterSource.VersionsByMediaType() )
+ {
+ var parameterName = parameterSource.GetParameterName( MediaTypeParameter );
+
+ if ( !string.IsNullOrEmpty( parameterName ) )
+ {
+ requestDelegate = EnsureRequestDelegate( requestDelegate, endpointBuilder.RequestDelegate );
+ requestDelegate = new ContentTypeApiVersionDecorator( requestDelegate, parameterName );
+ endpointBuilder.RequestDelegate = requestDelegate;
+ }
+ }
+ }
+
+ private static bool IsApiVersionNeutral( IList metadata )
+ {
+ var versionNeutral = false;
+
+ for ( var i = metadata.Count - 1; i >= 0; i-- )
+ {
+ if ( metadata[i] is IApiVersionNeutral )
+ {
+ versionNeutral = true;
+ metadata.RemoveAt( i );
+ break;
+ }
+ }
+
+ if ( versionNeutral )
+ {
+ for ( var i = metadata.Count - 1; i >= 0; i-- )
+ {
+ switch ( metadata[i] )
+ {
+ case IApiVersionProvider:
+ case IApiVersionNeutral:
+ metadata.RemoveAt( i );
+ break;
+ }
+ }
+ }
+
+ return versionNeutral;
+ }
+
+ private static bool ReportApiVersions( IList metadata )
+ {
+ var result = false;
+
+ for ( var i = metadata.Count - 1; i >= 0; i-- )
+ {
+ if ( metadata[i] is IReportApiVersions )
+ {
+ result = true;
+ metadata.RemoveAt( i );
+ }
+ }
+
+ return result;
+ }
+
+ private static ApiVersionSet? GetApiVersionSet( IList metadata )
+ {
+ for ( var i = metadata.Count - 1; i >= 0; i-- )
+ {
+ if ( metadata[i] is ApiVersionSet versionSet )
+ {
+ metadata.RemoveAt( i );
+ return versionSet;
+ }
+ }
+
+ return default;
+ }
+
+ private static bool TryGetApiVersions( IList metadata, out ApiVersionBuckets buckets )
+ {
+ if ( IsApiVersionNeutral( metadata ) )
+ {
+ buckets = default;
+ return false;
+ }
+
+ var mapped = default( SortedSet );
+ var supported = default( SortedSet );
+ var deprecated = default( SortedSet );
+ var advertised = default( SortedSet );
+ var deprecatedAdvertised = default( SortedSet );
+
+ for ( var i = metadata.Count - 1; i >= 0; i-- )
+ {
+ var item = metadata[i];
+
+ if ( item is not IApiVersionProvider provider )
+ {
+ continue;
+ }
+
+ metadata.RemoveAt( i );
+
+ var versions = provider.Versions;
+ var target = provider.Options switch
+ {
+ None => supported ??= [],
+ Mapped => mapped ??= [],
+ Deprecated => deprecated ??= [],
+ Advertised => advertised ??= [],
+ Advertised | Deprecated => deprecatedAdvertised ??= [],
+ _ => default,
+ };
+
+ if ( target is null )
+ {
+ continue;
+ }
+
+ for ( var j = 0; j < versions.Count; j++ )
+ {
+ target.Add( versions[j] );
+ }
+ }
+
+ buckets = new(
+ mapped?.ToArray() ?? [],
+ supported?.ToArray() ?? [],
+ deprecated?.ToArray() ?? [],
+ advertised?.ToArray() ?? [],
+ deprecatedAdvertised?.ToArray() ?? [] );
+
+ return true;
+ }
+
+ private static ApiVersionMetadata Build( IList metadata, ApiVersionSet versionSet, ApiVersioningOptions options )
+ {
+ var name = versionSet.Name;
+ ApiVersionModel? apiModel;
+
+ if ( !TryGetApiVersions( metadata, out var buckets ) ||
+ ( apiModel = versionSet.Build( options ) ).IsApiVersionNeutral )
+ {
+ if ( string.IsNullOrEmpty( name ) )
+ {
+ return ApiVersionMetadata.Neutral;
+ }
+
+ return new( ApiVersionModel.Neutral, ApiVersionModel.Neutral, name );
+ }
+
+ ApiVersionModel endpointModel;
+ ApiVersion[] emptyVersions;
+ var inheritedSupported = apiModel.SupportedApiVersions;
+ var inheritedDeprecated = apiModel.DeprecatedApiVersions;
+
+ if ( buckets.AreEmpty )
+ {
+ var noInheritedApiVersions = inheritedSupported.Count == 0 &&
+ inheritedDeprecated.Count == 0;
+
+ if ( noInheritedApiVersions )
+ {
+ endpointModel = ApiVersionModel.Empty;
+ }
+ else
+ {
+ emptyVersions = [];
+ endpointModel = new(
+ declaredVersions: emptyVersions,
+ inheritedSupported,
+ inheritedDeprecated,
+ emptyVersions,
+ emptyVersions );
+ }
+ }
+ else
+ {
+ var (mapped, supported, deprecated, advertised, advertisedDeprecated) = buckets;
+
+ if ( mapped.Count == 0 )
+ {
+ endpointModel = new(
+ declaredVersions: supported.Union( deprecated ),
+ supported.Union( inheritedSupported ),
+ deprecated.Union( inheritedDeprecated ),
+ advertised,
+ advertisedDeprecated );
+ }
+ else
+ {
+ emptyVersions = [];
+ endpointModel = new(
+ declaredVersions: mapped,
+ supportedVersions: inheritedSupported,
+ deprecatedVersions: inheritedDeprecated,
+ advertisedVersions: emptyVersions,
+ deprecatedAdvertisedVersions: emptyVersions );
+ }
+ }
+
+ return new( apiModel, endpointModel, name );
+ }
+
+ [MethodImpl( MethodImplOptions.AggressiveInlining )]
+ private static RequestDelegate EnsureRequestDelegate( RequestDelegate? current, RequestDelegate? original ) =>
+ ( current ?? original ) ??
+ throw new InvalidOperationException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ Format.UnsetRequestDelegate,
+ nameof( RequestDelegate ),
+ nameof( RouteEndpoint ) ) );
+
+ private record struct ApiVersionBuckets(
+ IReadOnlyList Mapped,
+ IReadOnlyList Supported,
+ IReadOnlyList Deprecated,
+ IReadOnlyList Advertised,
+ IReadOnlyList AdvertisedDeprecated )
+ {
+ internal readonly bool AreEmpty = Mapped.Count == 0
+ && Supported.Count == 0
+ && Deprecated.Count == 0
+ && Advertised.Count == 0
+ && AdvertisedDeprecated.Count == 0;
+ }
+}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IApiVersionSetBuilderFactory.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IApiVersionSetBuilderFactory.cs
deleted file mode 100644
index 2b96db7e..00000000
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IApiVersionSetBuilderFactory.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-
-namespace Asp.Versioning.Builder;
-
-///
-/// Defines the behavior of a factory to create API version set builders.
-///
-public interface IApiVersionSetBuilderFactory
-{
- ///
- /// Creates and returns a new API version set builder.
- ///
- /// The name of the API associated with the builder, if any.
- /// A new API version set builder .
- ApiVersionSetBuilder Create( string? name = default );
-}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs
index a37fac92..15525d66 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs
@@ -4,14 +4,10 @@ namespace Microsoft.AspNetCore.Builder;
using Asp.Versioning;
using Asp.Versioning.Builder;
-using Asp.Versioning.Routing;
using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Routing;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
+using System.Collections;
using System.Globalization;
-using System.Runtime.CompilerServices;
-using static Asp.Versioning.ApiVersionParameterLocation;
+using System.Runtime.Serialization;
using static Asp.Versioning.ApiVersionProviderOptions;
///
@@ -32,298 +28,501 @@ public static TBuilder WithApiVersionSet(
ApiVersionSet apiVersionSet )
where TBuilder : notnull, IEndpointConventionBuilder
{
- if ( apiVersionSet == null )
- {
- throw new ArgumentNullException( nameof( apiVersionSet ) );
- }
+ ArgumentNullException.ThrowIfNull( apiVersionSet );
- builder.Add( endpoint => endpoint.Metadata.Add( apiVersionSet ) );
- builder.Finally( FinalizeEndpoints );
+ builder.Add( endpoint => AddMetadata( endpoint, apiVersionSet ) );
+ builder.Finally( EndpointBuilderFinalizer.FinalizeEndpoints );
return builder;
}
///
- /// Applies the specified API version set to the endpoint group.
+ /// Indicates that the specified API version is mapped to the configured endpoint.
///
- /// The type of builder.
- /// The extended builder.
- /// The optional name associated with the builder.
- /// A new instance.
- public static IVersionedEndpointRouteBuilder WithApiVersionSet( this TBuilder builder, string? name = default )
- where TBuilder : notnull, IEndpointRouteBuilder, IEndpointConventionBuilder
- {
- if ( builder is IVersionedEndpointRouteBuilder versionedBuilder )
- {
- return versionedBuilder;
- }
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The major version number.
+ /// The optional minor version number.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder MapToApiVersion( this TBuilder builder, int majorVersion, int? minorVersion = default, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.MapToApiVersion( new ApiVersion( majorVersion, minorVersion, status ) );
- var factory = builder.ServiceProvider.GetRequiredService();
+ ///
+ /// Indicates that the specified API version is mapped to the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The version number.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder MapToApiVersion( this TBuilder builder, double version, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.MapToApiVersion( new ApiVersion( version, status ) );
- versionedBuilder = new VersionedEndpointRouteBuilder( builder, builder, factory.Create( name ) );
- builder.Finally( FinalizeRoutes );
+ ///
+ /// Indicates that the specified API version is mapped to the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The version year.
+ /// The version month.
+ /// The version day.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder MapToApiVersion( this TBuilder builder, int year, int month, int day, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.MapToApiVersion( new ApiVersion( new DateOnly( year, month, day ), status ) );
+
+ ///
+ /// Indicates that the specified API version is mapped to the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The group version.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder MapToApiVersion( this TBuilder builder, DateOnly groupVersion, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.MapToApiVersion( new ApiVersion( groupVersion, status ) );
- return versionedBuilder;
+ ///
+ /// Maps the specified API version to the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The API version to map to the endpoint.
+ /// The original .
+ public static TBuilder MapToApiVersion( this TBuilder builder, ApiVersion apiVersion )
+ where TBuilder : notnull, IEndpointConventionBuilder
+ {
+ builder.Add( endpoint => AddMetadata( endpoint, Convention.MapToApiVersion( apiVersion ) ) );
+ return builder;
}
- private static void FinalizeEndpoints( EndpointBuilder endpointBuilder )
+ ///
+ /// Indicates that the endpoint is API version-neutral.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The original .
+ public static TBuilder IsApiVersionNeutral( this TBuilder builder )
+ where TBuilder : notnull, IEndpointConventionBuilder
{
- var versionSet = GetApiVersionSet( endpointBuilder.Metadata );
- Finialize( endpointBuilder, versionSet );
+ builder.Add( endpoint => AddMetadata( endpoint, new ApiVersionNeutralAttribute() ) );
+ return builder;
}
- private static void FinalizeRoutes( EndpointBuilder endpointBuilder )
+ ///
+ /// Indicates that the specified API version is supported by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The major version number.
+ /// The optional minor version number.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder HasApiVersion( this TBuilder builder, int majorVersion, int? minorVersion = default, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.HasApiVersion( new ApiVersion( majorVersion, minorVersion, status ) );
+
+ ///
+ /// Indicates that the specified API version is supported by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The version number.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder HasApiVersion( this TBuilder builder, double version, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.HasApiVersion( new ApiVersion( version, status ) );
+
+ ///
+ /// Indicates that the specified API version is supported by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The version year.
+ /// The version month.
+ /// The version day.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder HasApiVersion( this TBuilder builder, int year, int month, int day, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.HasApiVersion( new ApiVersion( new DateOnly( year, month, day ), status ) );
+
+ ///
+ /// Indicates that the specified API version is supported by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The group version.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder HasApiVersion( this TBuilder builder, DateOnly groupVersion, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.HasApiVersion( new ApiVersion( groupVersion, status ) );
+
+ ///
+ /// Indicates that the specified API version is supported by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The supported API version implemented by the endpoint.
+ /// The original .
+ public static TBuilder HasApiVersion( this TBuilder builder, ApiVersion apiVersion )
+ where TBuilder : notnull, IEndpointConventionBuilder
{
- var versionSet = endpointBuilder.ApplicationServices.GetService();
- Finialize( endpointBuilder, versionSet );
+ builder.Add(
+ endpoint =>
+ {
+ AddMetadata( endpoint, Convention.HasApiVersion( apiVersion ) );
+ AdvertiseInApiVersionSet( endpoint.Metadata, apiVersion );
+ } );
+
+ return builder;
}
- private static void Finialize( EndpointBuilder endpointBuilder, ApiVersionSet? versionSet )
+ ///
+ /// Indicates that the specified API version is deprecated by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The major version number.
+ /// The optional minor version number.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder HasDeprecatedApiVersion( this TBuilder builder, int majorVersion, int? minorVersion = default, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.HasDeprecatedApiVersion( new ApiVersion( majorVersion, minorVersion, status ) );
+
+ ///
+ /// Indicates that the specified API version is deprecated by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The version number.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder HasDeprecatedApiVersion( this TBuilder builder, double version, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.HasDeprecatedApiVersion( new ApiVersion( version, status ) );
+
+ ///
+ /// Indicates that the specified API version is deprecated by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The version year.
+ /// The version month.
+ /// The version day.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder HasDeprecatedApiVersion( this TBuilder builder, int year, int month, int day, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.HasDeprecatedApiVersion( new ApiVersion( new DateOnly( year, month, day ), status ) );
+
+ ///
+ /// Indicates that the specified API version is deprecated by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The group version.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder HasDeprecatedApiVersion( this TBuilder builder, DateOnly groupVersion, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.HasDeprecatedApiVersion( new ApiVersion( groupVersion, status ) );
+
+ ///
+ /// Indicates that the specified API version is deprecated by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The deprecated API version implemented by the endpoint.
+ /// The original .
+ public static TBuilder HasDeprecatedApiVersion( this TBuilder builder, ApiVersion apiVersion )
+ where TBuilder : notnull, IEndpointConventionBuilder
{
- if ( versionSet is null )
- {
- // this should be impossible because WithApiVersionSet had to be called to get here
- endpointBuilder.Metadata.Add( ApiVersionMetadata.Empty );
- return;
- }
+ builder.Add(
+ endpoint =>
+ {
+ AddMetadata( endpoint, Convention.HasDeprecatedApiVersion( apiVersion ) );
+ AdvertiseDeprecatedInApiVersionSet( endpoint.Metadata, apiVersion );
+ } );
- var services = endpointBuilder.ApplicationServices;
- var endpointMetadata = endpointBuilder.Metadata;
- var options = services.GetRequiredService>().Value;
- var metadata = Build( endpointMetadata, versionSet, options );
- var reportApiVersions = ReportApiVersions( endpointMetadata ) ||
- options.ReportApiVersions ||
- versionSet.ReportApiVersions;
+ return builder;
+ }
- endpointBuilder.Metadata.Add( metadata );
+ ///
+ /// Indicates that the specified API version is advertised by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The major version number.
+ /// The optional minor version number.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder AdvertisesApiVersion( this TBuilder builder, int majorVersion, int? minorVersion = default, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.AdvertisesApiVersion( new ApiVersion( majorVersion, minorVersion, status ) );
- var requestDelegate = default( RequestDelegate );
+ ///
+ /// Indicates that the specified API version is advertised by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The version number.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder AdvertisesApiVersion( this TBuilder builder, double version, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.AdvertisesApiVersion( new ApiVersion( version, status ) );
- if ( reportApiVersions )
- {
- requestDelegate = EnsureRequestDelegate( requestDelegate, endpointBuilder.RequestDelegate );
- requestDelegate = new ReportApiVersionsDecorator( requestDelegate, metadata );
- endpointBuilder.RequestDelegate = requestDelegate;
- }
+ ///
+ /// Indicates that the specified API version is advertised by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The version year.
+ /// The version month.
+ /// The version day.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder AdvertisesApiVersion( this TBuilder builder, int year, int month, int day, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.AdvertisesApiVersion( new ApiVersion( new DateOnly( year, month, day ), status ) );
- var parameterSource = endpointBuilder.ApplicationServices.GetRequiredService();
+ ///
+ /// Indicates that the specified API version is advertised by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The group version.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder AdvertisesApiVersion( this TBuilder builder, DateOnly groupVersion, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.AdvertisesApiVersion( new ApiVersion( groupVersion, status ) );
- if ( parameterSource.VersionsByMediaType() )
- {
- var parameterName = parameterSource.GetParameterName( MediaTypeParameter );
+ ///
+ /// Indicates that the specified API version is advertised by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The advertised API version not directly implemented by the endpoint.
+ /// The original .
+ public static TBuilder AdvertisesApiVersion( this TBuilder builder, ApiVersion apiVersion )
+ where TBuilder : notnull, IEndpointConventionBuilder
+ {
+ builder.Add(
+ endpoint =>
+ {
+ AddMetadata( endpoint, Convention.AdvertisesApiVersion( apiVersion ) );
+ AdvertiseInApiVersionSet( endpoint.Metadata, apiVersion );
+ } );
+
+ return builder;
+ }
- if ( !string.IsNullOrEmpty( parameterName ) )
+ ///
+ /// Indicates that the specified API version is advertised and deprecated by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The major version number.
+ /// The optional minor version number.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder AdvertisesDeprecatedApiVersion( this TBuilder builder, int majorVersion, int? minorVersion = default, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.AdvertisesDeprecatedApiVersion( new ApiVersion( majorVersion, minorVersion, status ) );
+
+ ///
+ /// Indicates that the specified API version is advertised and deprecated by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The version number.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder AdvertisesDeprecatedApiVersion( this TBuilder builder, double version, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.AdvertisesDeprecatedApiVersion( new ApiVersion( version, status ) );
+
+ ///
+ /// Indicates that the specified API version is advertised and deprecated by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The version year.
+ /// The version month.
+ /// The version day.
+ /// The version status.
+ /// The original .
+ public static TBuilder AdvertisesDeprecatedApiVersion( this TBuilder builder, int year, int month, int day, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.AdvertisesDeprecatedApiVersion( new ApiVersion( new DateOnly( year, month, day ), status ) );
+
+ ///
+ /// Indicates that the specified API version is advertised and deprecated by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The group version.
+ /// The optional version status.
+ /// The original .
+ public static TBuilder AdvertisesDeprecatedApiVersion( this TBuilder builder, DateOnly groupVersion, string? status = default )
+ where TBuilder : notnull, IEndpointConventionBuilder => builder.AdvertisesDeprecatedApiVersion( new ApiVersion( groupVersion, status ) );
+
+ ///
+ /// Indicates that the specified API version is advertised and deprecated by the configured endpoint.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The advertised, but deprecated API version not directly implemented by the endpoint.
+ /// The original .
+ public static TBuilder AdvertisesDeprecatedApiVersion( this TBuilder builder, ApiVersion apiVersion )
+ where TBuilder : notnull, IEndpointConventionBuilder
+ {
+ builder.Add(
+ endpoint =>
{
- requestDelegate = EnsureRequestDelegate( requestDelegate, endpointBuilder.RequestDelegate );
- requestDelegate = new ContentTypeApiVersionDecorator( requestDelegate, parameterName );
- endpointBuilder.RequestDelegate = requestDelegate;
- }
- }
+ AddMetadata( endpoint, Convention.AdvertisesDeprecatedApiVersion( apiVersion ) );
+ AdvertiseDeprecatedInApiVersionSet( endpoint.Metadata, apiVersion );
+ } );
+
+ return builder;
}
- private static bool IsApiVersionNeutral( IList metadata )
+ ///
+ /// Indicates that the endpoint will report its API versions.
+ ///
+ /// The extended type.
+ /// The extended endpoint convention builder.
+ /// The original .
+ public static TBuilder ReportApiVersions( this TBuilder builder )
+ where TBuilder : notnull, IEndpointConventionBuilder
{
- var versionNeutral = false;
+ builder.Add( endpoint => AddMetadata( endpoint, Convention.ReportApiVersions ) );
+ return builder;
+ }
- for ( var i = metadata.Count - 1; i >= 0; i-- )
+ private static void AddMetadata( EndpointBuilder builder, ApiVersionSet versionSet )
+ {
+ var metadata = builder.Metadata;
+ var grouped = builder.ApplicationServices.GetService( typeof( ApiVersionSetBuilder ) ) is not null;
+
+ if ( grouped )
{
- if ( metadata[i] is IApiVersionNeutral )
- {
- versionNeutral = true;
- metadata.RemoveAt( i );
- break;
- }
+ throw new InvalidOperationException( SR.MultipleVersionSets );
}
- if ( versionNeutral )
+ for ( var i = 0; i < metadata.Count; i++ )
{
- for ( var i = metadata.Count - 1; i >= 0; i-- )
+ if ( metadata[i] is ApiVersionSet )
{
- switch ( metadata[i] )
- {
- case IApiVersionProvider:
- case IApiVersionNeutral:
- metadata.RemoveAt( i );
- break;
- }
+ throw new InvalidOperationException( SR.MultipleVersionSets );
}
}
- return versionNeutral;
+ metadata.Add( versionSet );
+
+ if ( !string.IsNullOrEmpty( versionSet.Name ) )
+ {
+ metadata.Insert( 0, new TagsAttribute( versionSet.Name ) );
+ }
}
- private static bool ReportApiVersions( IList metadata )
+ private static void AddMetadata( EndpointBuilder builder, object item )
{
- var result = false;
+ var metadata = builder.Metadata;
+ var grouped = builder.ApplicationServices.GetService( typeof( ApiVersionSetBuilder ) ) is not null;
+
+ metadata.Add( item );
+
+ if ( grouped )
+ {
+ return;
+ }
for ( var i = metadata.Count - 1; i >= 0; i-- )
{
- if ( metadata[i] is IReportApiVersions )
+ if ( metadata[i] is ApiVersionSet )
{
- result = true;
- metadata.RemoveAt( i );
+ return;
}
}
- return result;
+ throw new InvalidOperationException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ Format.NoVersionSet,
+ builder.DisplayName,
+ nameof( IEndpointRouteBuilderExtensions.NewVersionedApi ),
+ nameof( IEndpointRouteBuilderExtensions.WithApiVersionSet ) ) );
}
- private static ApiVersionSet? GetApiVersionSet( IList metadata )
+ private static void AdvertiseInApiVersionSet( IList metadata, ApiVersion apiVersion )
{
- var result = default( ApiVersionSet );
-
for ( var i = metadata.Count - 1; i >= 0; i-- )
{
- if ( metadata[i] is ApiVersionSet set )
+ if ( metadata[i] is ApiVersionSet versionSet )
{
- result ??= set;
- metadata.RemoveAt( i );
+ versionSet.AdvertisesApiVersion( apiVersion );
+ break;
}
}
-
- return result;
}
- private static bool TryGetApiVersions( IList metadata, out ApiVersionBuckets buckets )
+ private static void AdvertiseDeprecatedInApiVersionSet( IList metadata, ApiVersion apiVersion )
{
- if ( IsApiVersionNeutral( metadata ) )
- {
- buckets = default;
- return false;
- }
-
- var mapped = default( SortedSet );
- var supported = default( SortedSet );
- var deprecated = default( SortedSet );
- var advertised = default( SortedSet );
- var deprecatedAdvertised = default( SortedSet );
-
for ( var i = metadata.Count - 1; i >= 0; i-- )
{
- var item = metadata[i];
-
- if ( item is not IApiVersionProvider provider )
+ if ( metadata[i] is ApiVersionSet versionSet )
{
- continue;
+ versionSet.AdvertisesDeprecatedApiVersion( apiVersion );
+ break;
}
+ }
+ }
- metadata.RemoveAt( i );
+ private sealed class SingleItemReadOnlyList : IReadOnlyList
+ {
+ private readonly ApiVersion item;
- var versions = provider.Versions;
- var target = provider.Options switch
- {
- None => supported ??= new(),
- Mapped => mapped ??= new(),
- Deprecated => deprecated ??= new(),
- Advertised => advertised ??= new(),
- Advertised | Deprecated => deprecatedAdvertised ??= new(),
- _ => default,
- };
-
- if ( target is null )
- {
- continue;
- }
+ internal SingleItemReadOnlyList( ApiVersion item ) => this.item = item;
- for ( var j = 0; j < versions.Count; j++ )
- {
- target.Add( versions[j] );
- }
+#pragma warning disable IDE0079
+#pragma warning disable CA2201 // Do not raise reserved exception types
+ public ApiVersion this[int index] => index == 0 ? item : throw new IndexOutOfRangeException();
+#pragma warning restore CA2201 // Do not raise reserved exception types
+#pragma warning restore IDE0079
+
+ public int Count => 1;
+
+ public IEnumerator GetEnumerator()
+ {
+ yield return item;
}
- buckets = new(
- mapped?.ToArray() ?? Array.Empty(),
- supported?.ToArray() ?? Array.Empty(),
- deprecated?.ToArray() ?? Array.Empty(),
- advertised?.ToArray() ?? Array.Empty(),
- deprecatedAdvertised?.ToArray() ?? Array.Empty() );
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ }
+
+ private sealed class ReportApiVersionsConvention : IReportApiVersions
+ {
+ public ApiVersionMapping Mapping => ApiVersionMapping.None;
- return true;
+ public void Report( HttpResponse response, ApiVersionModel apiVersionModel ) { }
}
- private static ApiVersionMetadata Build( IList metadata, ApiVersionSet versionSet, ApiVersioningOptions options )
+ private sealed class Convention : IApiVersionProvider
{
- var name = versionSet.Name;
- ApiVersionModel? apiModel;
+ private static ReportApiVersionsConvention? reportApiVersions;
- if ( !TryGetApiVersions( metadata, out var buckets ) ||
- ( apiModel = versionSet.Build( options ) ).IsApiVersionNeutral )
+ private Convention( ApiVersion version, ApiVersionProviderOptions options )
{
- if ( string.IsNullOrEmpty( name ) )
- {
- return ApiVersionMetadata.Neutral;
- }
-
- return new( ApiVersionModel.Neutral, ApiVersionModel.Neutral, name );
+ Versions = new SingleItemReadOnlyList( version );
+ Options = options;
}
- ApiVersionModel endpointModel;
- ApiVersion[] emptyVersions;
- var inheritedSupported = apiModel.SupportedApiVersions;
- var inheritedDeprecated = apiModel.DeprecatedApiVersions;
- var (mapped, supported, deprecated, advertised, advertisedDeprecated) = buckets;
- var isEmpty = mapped.Count == 0 &&
- supported.Count == 0 &&
- deprecated.Count == 0 &&
- advertised.Count == 0 &&
- advertisedDeprecated.Count == 0;
-
- if ( isEmpty )
- {
- var noInheritedApiVersions = inheritedSupported.Count == 0 &&
- inheritedDeprecated.Count == 0;
+ public ApiVersionProviderOptions Options { get; }
- if ( noInheritedApiVersions )
- {
- endpointModel = ApiVersionModel.Empty;
- }
- else
- {
- emptyVersions = Array.Empty();
- endpointModel = new(
- declaredVersions: emptyVersions,
- inheritedSupported,
- inheritedDeprecated,
- emptyVersions,
- emptyVersions );
- }
- }
- else if ( mapped.Count == 0 )
- {
- endpointModel = new(
- declaredVersions: supported.Union( deprecated ),
- supported.Union( inheritedSupported ),
- deprecated.Union( inheritedDeprecated ),
- advertised,
- advertisedDeprecated );
- }
- else
- {
- emptyVersions = Array.Empty();
- endpointModel = new(
- declaredVersions: mapped,
- supportedVersions: inheritedSupported,
- deprecatedVersions: inheritedDeprecated,
- advertisedVersions: emptyVersions,
- deprecatedAdvertisedVersions: emptyVersions );
- }
+ public IReadOnlyList Versions { get; }
- return new( apiModel, endpointModel, name );
- }
+ internal static IReportApiVersions ReportApiVersions => reportApiVersions ??= new();
- private static RequestDelegate EnsureRequestDelegate( RequestDelegate? current, RequestDelegate? original ) =>
- ( current ?? original ) ??
- throw new InvalidOperationException(
- string.Format(
- CultureInfo.CurrentCulture,
- SR.UnsetRequestDelegate,
- nameof( RequestDelegate ),
- nameof( RouteEndpoint ) ) );
-
- private record struct ApiVersionBuckets(
- IReadOnlyList Mapped,
- IReadOnlyList Supported,
- IReadOnlyList Deprecated,
- IReadOnlyList Advertised,
- IReadOnlyList AdvertisedDeprecated );
+ internal static Convention HasApiVersion( ApiVersion version ) => new( version, None );
+
+ internal static Convention HasDeprecatedApiVersion( ApiVersion version ) => new( version, Deprecated );
+
+ internal static Convention MapToApiVersion( ApiVersion version ) => new( version, Mapped );
+
+ internal static Convention AdvertisesApiVersion( ApiVersion version ) => new( version, Advertised );
+
+ internal static Convention AdvertisesDeprecatedApiVersion( ApiVersion version ) => new( version, Advertised | Deprecated );
+ }
}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointRouteBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointRouteBuilderExtensions.cs
index 65664447..e6054e34 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointRouteBuilderExtensions.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointRouteBuilderExtensions.cs
@@ -2,9 +2,12 @@
namespace Microsoft.AspNetCore.Builder;
+using Asp.Versioning;
using Asp.Versioning.Builder;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
+using System.Runtime.CompilerServices;
///
/// Provides extension methods for .
@@ -20,13 +23,86 @@ public static class IEndpointRouteBuilderExtensions
/// A new API version set builder .
public static ApiVersionSetBuilder NewApiVersionSet( this IEndpointRouteBuilder endpoints, string? name = default )
{
- if ( endpoints == null )
+ ArgumentNullException.ThrowIfNull( endpoints );
+ var create = endpoints.ServiceProvider.GetService();
+ return create is null ? new( name ) : create( name );
+ }
+
+ ///
+ /// Applies the specified API version set to the endpoint group.
+ ///
+ /// The type of builder.
+ /// The extended builder.
+ /// The optional name associated with the builder.
+ /// A new instance.
+ public static IVersionedEndpointRouteBuilder WithApiVersionSet( this TBuilder builder, string? name = default )
+ where TBuilder : notnull, IEndpointRouteBuilder, IEndpointConventionBuilder
+ {
+ ArgumentNullException.ThrowIfNull( builder );
+
+ if ( builder.HasMetadata() )
{
- throw new ArgumentNullException( nameof( endpoints ) );
+ throw new InvalidOperationException( SR.CannotNestVersionSet );
}
- var factory = endpoints.ServiceProvider.GetRequiredService();
+ if ( !string.IsNullOrEmpty( name ) )
+ {
+ builder.Add( endpoint => endpoint.Metadata.Insert( 0, new TagsAttribute( name ) ) );
+ }
- return factory.Create( name );
+ builder.Finally( EndpointBuilderFinalizer.FinalizeRoutes );
+
+ return builder.NewVersionedEndpointRouteBuilder( builder, builder, name );
}
+
+ ///
+ /// Creates a route group builder for defining all versioned endpoints in an API.
+ ///
+ /// The extended .
+ /// The optional name associated with the builder.
+ /// A new instance.
+ public static IVersionedEndpointRouteBuilder NewVersionedApi( this IEndpointRouteBuilder builder, string? name = default )
+ {
+ ArgumentNullException.ThrowIfNull( builder );
+
+ if ( builder.IsNestedGroup() )
+ {
+ throw new InvalidOperationException( SR.CannotNestApiGroup );
+ }
+
+ var group = builder.MapGroup( string.Empty );
+ IEndpointConventionBuilder convention = group;
+
+ if ( !string.IsNullOrEmpty( name ) )
+ {
+ convention.Add( endpoint => endpoint.Metadata.Insert( 0, new TagsAttribute( name ) ) );
+ }
+
+ convention.Finally( EndpointBuilderFinalizer.FinalizeRoutes );
+
+ return builder.NewVersionedEndpointRouteBuilder( group, group, name );
+ }
+
+ [MethodImpl( MethodImplOptions.AggressiveInlining )]
+ private static IVersionedEndpointRouteBuilder NewVersionedEndpointRouteBuilder(
+ this IEndpointRouteBuilder builder,
+ IEndpointRouteBuilder routeBuilder,
+ IEndpointConventionBuilder conventionBuilder,
+ string? name )
+ {
+ var create = builder.ServiceProvider.GetService();
+ var versionSet = builder.NewApiVersionSet( name );
+
+ return create is null ?
+ new VersionedEndpointRouteBuilder( routeBuilder, conventionBuilder, versionSet ) :
+ create( routeBuilder, conventionBuilder, versionSet );
+ }
+
+ [MethodImpl( MethodImplOptions.AggressiveInlining )]
+ private static bool HasMetadata( this IEndpointRouteBuilder builder ) =>
+ builder.ServiceProvider.GetService() is not null;
+
+ [MethodImpl( MethodImplOptions.AggressiveInlining )]
+ private static bool IsNestedGroup( this IEndpointRouteBuilder builder ) =>
+ builder is RouteGroupBuilder || builder.HasMetadata();
}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/RouteHandlerBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/RouteHandlerBuilderExtensions.cs
deleted file mode 100644
index c8038c68..00000000
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/RouteHandlerBuilderExtensions.cs
+++ /dev/null
@@ -1,417 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-
-namespace Microsoft.AspNetCore.Builder;
-
-using Asp.Versioning;
-using Asp.Versioning.Builder;
-using Microsoft.AspNetCore.Http;
-using System.Collections;
-using static Asp.Versioning.ApiVersionProviderOptions;
-
-///
-/// Provides extension methods for .
-///
-[CLSCompliant( false )]
-public static class RouteHandlerBuilderExtensions
-{
- ///
- /// Indicates that the specified API version is mapped to the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The major version number.
- /// The optional minor version number.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder MapToApiVersion( this RouteHandlerBuilder builder, int majorVersion, int? minorVersion = default, string? status = default ) =>
- builder.MapToApiVersion( new ApiVersion( majorVersion, minorVersion, status ) );
-
- ///
- /// Indicates that the specified API version is mapped to the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The version number.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder MapToApiVersion( this RouteHandlerBuilder builder, double version, string? status = default ) =>
- builder.MapToApiVersion( new ApiVersion( version, status ) );
-
- ///
- /// Indicates that the specified API version is mapped to the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The version year.
- /// The version month.
- /// The version day.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder MapToApiVersion( this RouteHandlerBuilder builder, int year, int month, int day, string? status = default ) =>
- builder.MapToApiVersion( new ApiVersion( new DateOnly( year, month, day ), status ) );
-
- ///
- /// Indicates that the specified API version is mapped to the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The group version.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder MapToApiVersion( this RouteHandlerBuilder builder, DateOnly groupVersion, string? status = default ) =>
- builder.MapToApiVersion( new ApiVersion( groupVersion, status ) );
-
- ///
- /// Maps the specified API version to the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The API version to map to the endpoint.
- /// The original .
- public static RouteHandlerBuilder MapToApiVersion( this RouteHandlerBuilder builder, ApiVersion apiVersion )
- {
- builder.Add( endpoint => endpoint.Metadata.Add( Convention.MapToApiVersion( apiVersion ) ) );
- return builder;
- }
-
- ///
- /// Indicates that the endpoint is API version-neutral.
- ///
- /// The extended route handler builder .
- /// The original .
- public static RouteHandlerBuilder IsApiVersionNeutral( this RouteHandlerBuilder builder )
- {
- builder.Add( endpoint => endpoint.Metadata.Add( new ApiVersionNeutralAttribute() ) );
- return builder;
- }
-
- ///
- /// Indicates that the specified API version is supported by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The major version number.
- /// The optional minor version number.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder HasApiVersion( this RouteHandlerBuilder builder, int majorVersion, int? minorVersion = default, string? status = default ) =>
- builder.HasApiVersion( new ApiVersion( majorVersion, minorVersion, status ) );
-
- ///
- /// Indicates that the specified API version is supported by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The version number.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder HasApiVersion( this RouteHandlerBuilder builder, double version, string? status = default ) =>
- builder.HasApiVersion( new ApiVersion( version, status ) );
-
- ///
- /// Indicates that the specified API version is supported by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The version year.
- /// The version month.
- /// The version day.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder HasApiVersion( this RouteHandlerBuilder builder, int year, int month, int day, string? status = default ) =>
- builder.HasApiVersion( new ApiVersion( new DateOnly( year, month, day ), status ) );
-
- ///
- /// Indicates that the specified API version is supported by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The group version.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder HasApiVersion( this RouteHandlerBuilder builder, DateOnly groupVersion, string? status = default ) =>
- builder.HasApiVersion( new ApiVersion( groupVersion, status ) );
-
- ///
- /// Indicates that the specified API version is supported by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The supported API version implemented by the endpoint.
- /// The original .
- public static RouteHandlerBuilder HasApiVersion( this RouteHandlerBuilder builder, ApiVersion apiVersion )
- {
- builder.Add(
- endpoint =>
- {
- var metadata = endpoint.Metadata;
- metadata.Add( Convention.HasApiVersion( apiVersion ) );
- AdvertiseInApiVersionSet( metadata, apiVersion );
- } );
-
- return builder;
- }
-
- ///
- /// Indicates that the specified API version is deprecated by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The major version number.
- /// The optional minor version number.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder HasDeprecatedApiVersion( this RouteHandlerBuilder builder, int majorVersion, int? minorVersion = default, string? status = default ) =>
- builder.HasDeprecatedApiVersion( new ApiVersion( majorVersion, minorVersion, status ) );
-
- ///
- /// Indicates that the specified API version is deprecated by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The version number.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder HasDeprecatedApiVersion( this RouteHandlerBuilder builder, double version, string? status = default ) =>
- builder.HasDeprecatedApiVersion( new ApiVersion( version, status ) );
-
- ///
- /// Indicates that the specified API version is deprecated by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The version year.
- /// The version month.
- /// The version day.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder HasDeprecatedApiVersion( this RouteHandlerBuilder builder, int year, int month, int day, string? status = default ) =>
- builder.HasDeprecatedApiVersion( new ApiVersion( new DateOnly( year, month, day ), status ) );
-
- ///
- /// Indicates that the specified API version is deprecated by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The group version.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder HasDeprecatedApiVersion( this RouteHandlerBuilder builder, DateOnly groupVersion, string? status = default ) =>
- builder.HasDeprecatedApiVersion( new ApiVersion( groupVersion, status ) );
-
- ///
- /// Indicates that the specified API version is deprecated by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The deprecated API version implemented by the endpoint.
- /// The original .
- public static RouteHandlerBuilder HasDeprecatedApiVersion( this RouteHandlerBuilder builder, ApiVersion apiVersion )
- {
- builder.Add(
- endpoint =>
- {
- var metadata = endpoint.Metadata;
- metadata.Add( Convention.HasDeprecatedApiVersion( apiVersion ) );
- AdvertiseDeprecatedInApiVersionSet( metadata, apiVersion );
- } );
-
- return builder;
- }
-
- ///
- /// Indicates that the specified API version is advertised by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The major version number.
- /// The optional minor version number.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder AdvertisesApiVersion( this RouteHandlerBuilder builder, int majorVersion, int? minorVersion = default, string? status = default ) =>
- builder.AdvertisesApiVersion( new ApiVersion( majorVersion, minorVersion, status ) );
-
- ///
- /// Indicates that the specified API version is advertised by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The version number.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder AdvertisesApiVersion( this RouteHandlerBuilder builder, double version, string? status = default ) =>
- builder.AdvertisesApiVersion( new ApiVersion( version, status ) );
-
- ///
- /// Indicates that the specified API version is advertised by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The version year.
- /// The version month.
- /// The version day.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder AdvertisesApiVersion( this RouteHandlerBuilder builder, int year, int month, int day, string? status = default ) =>
- builder.AdvertisesApiVersion( new ApiVersion( new DateOnly( year, month, day ), status ) );
-
- ///
- /// Indicates that the specified API version is advertised by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The group version.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder AdvertisesApiVersion( this RouteHandlerBuilder builder, DateOnly groupVersion, string? status = default ) =>
- builder.AdvertisesApiVersion( new ApiVersion( groupVersion, status ) );
-
- ///
- /// Indicates that the specified API version is advertised by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The advertised API version not directly implemented by the endpoint.
- /// The original .
- public static RouteHandlerBuilder AdvertisesApiVersion( this RouteHandlerBuilder builder, ApiVersion apiVersion )
- {
- builder.Add(
- endpoint =>
- {
- var metadata = endpoint.Metadata;
- metadata.Add( Convention.AdvertisesApiVersion( apiVersion ) );
- AdvertiseInApiVersionSet( metadata, apiVersion );
- } );
-
- return builder;
- }
-
- ///
- /// Indicates that the specified API version is advertised and deprecated by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The major version number.
- /// The optional minor version number.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder AdvertisesDeprecatedApiVersion( this RouteHandlerBuilder builder, int majorVersion, int? minorVersion = default, string? status = default ) =>
- builder.AdvertisesDeprecatedApiVersion( new ApiVersion( majorVersion, minorVersion, status ) );
-
- ///
- /// Indicates that the specified API version is advertised and deprecated by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The version number.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder AdvertisesDeprecatedApiVersion( this RouteHandlerBuilder builder, double version, string? status = default ) =>
- builder.AdvertisesDeprecatedApiVersion( new ApiVersion( version, status ) );
-
- ///
- /// Indicates that the specified API version is advertised and deprecated by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The version year.
- /// The version month.
- /// The version day.
- /// The version status.
- /// The original .
- public static RouteHandlerBuilder AdvertisesDeprecatedApiVersion( this RouteHandlerBuilder builder, int year, int month, int day, string? status = default ) =>
- builder.AdvertisesDeprecatedApiVersion( new ApiVersion( new DateOnly( year, month, day ), status ) );
-
- ///
- /// Indicates that the specified API version is advertised and deprecated by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The group version.
- /// The optional version status.
- /// The original .
- public static RouteHandlerBuilder AdvertisesDeprecatedApiVersion( this RouteHandlerBuilder builder, DateOnly groupVersion, string? status = default ) =>
- builder.AdvertisesDeprecatedApiVersion( new ApiVersion( groupVersion, status ) );
-
- ///
- /// Indicates that the specified API version is advertised and deprecated by the configured endpoint.
- ///
- /// The extended route handler builder .
- /// The advertised, but deprecated API version not directly implemented by the endpoint.
- /// The original .
- public static RouteHandlerBuilder AdvertisesDeprecatedApiVersion( this RouteHandlerBuilder builder, ApiVersion apiVersion )
- {
- builder.Add(
- endpoint =>
- {
- var metadata = endpoint.Metadata;
- metadata.Add( Convention.AdvertisesDeprecatedApiVersion( apiVersion ) );
- AdvertiseDeprecatedInApiVersionSet( metadata, apiVersion );
- } );
-
- return builder;
- }
-
- ///
- /// Indicates that the endpoint will report its API versions.
- ///
- /// The extended route handler builder .
- /// The original .
- public static RouteHandlerBuilder ReportApiVersions( this RouteHandlerBuilder builder )
- {
- builder.Add( endpoint => endpoint.Metadata.Add( Convention.ReportApiVersions ) );
- return builder;
- }
-
- private static void AdvertiseInApiVersionSet( IList metadata, ApiVersion apiVersion )
- {
- for ( var i = metadata.Count - 1; i >= 0; i-- )
- {
- if ( metadata[i] is ApiVersionSet versionSet )
- {
- versionSet.AdvertisesApiVersion( apiVersion );
- break;
- }
- }
- }
-
- private static void AdvertiseDeprecatedInApiVersionSet( IList metadata, ApiVersion apiVersion )
- {
- for ( var i = metadata.Count - 1; i >= 0; i-- )
- {
- if ( metadata[i] is ApiVersionSet versionSet )
- {
- versionSet.AdvertisesDeprecatedApiVersion( apiVersion );
- break;
- }
- }
- }
-
- private sealed class SingleItemReadOnlyList : IReadOnlyList
- {
- private readonly ApiVersion item;
-
- internal SingleItemReadOnlyList( ApiVersion item ) => this.item = item;
-
- public ApiVersion this[int index] => index == 0 ? item : throw new IndexOutOfRangeException();
-
- public int Count => 1;
-
- public IEnumerator GetEnumerator()
- {
- yield return item;
- }
-
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
- }
-
- private sealed class ReportApiVersionsConvention : IReportApiVersions
- {
- public ApiVersionMapping Mapping => ApiVersionMapping.None;
-
- public void Report( HttpResponse response, ApiVersionModel apiVersionModel ) { }
- }
-
- private sealed class Convention : IApiVersionProvider
- {
- private static ReportApiVersionsConvention? reportApiVersions;
-
- private Convention( ApiVersion version, ApiVersionProviderOptions options )
- {
- Versions = new SingleItemReadOnlyList( version );
- Options = options;
- }
-
- public ApiVersionProviderOptions Options { get; }
-
- public IReadOnlyList Versions { get; }
-
- internal static IReportApiVersions ReportApiVersions => reportApiVersions ??= new();
-
- internal static Convention HasApiVersion( ApiVersion version ) => new( version, None );
-
- internal static Convention HasDeprecatedApiVersion( ApiVersion version ) => new( version, Deprecated );
-
- internal static Convention MapToApiVersion( ApiVersion version ) => new( version, Mapped );
-
- internal static Convention AdvertisesApiVersion( ApiVersion version ) => new( version, Advertised );
-
- internal static Convention AdvertisesDeprecatedApiVersion( ApiVersion version ) => new( version, Advertised | Deprecated );
- }
-}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/VersionedEndpointRouteBuilder.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/VersionedEndpointRouteBuilder.cs
index 5971fe68..36af4a92 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/VersionedEndpointRouteBuilder.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/VersionedEndpointRouteBuilder.cs
@@ -46,7 +46,8 @@ public VersionedEndpointRouteBuilder(
protected ApiVersionSetBuilder VersionSetBuilder { get; }
///
- public virtual IApplicationBuilder CreateApplicationBuilder() => routeBuilder.CreateApplicationBuilder();
+ public virtual IApplicationBuilder CreateApplicationBuilder() =>
+ routeBuilder.CreateApplicationBuilder();
///
public virtual IServiceProvider ServiceProvider => serviceProvider;
@@ -55,22 +56,22 @@ public VersionedEndpointRouteBuilder(
public virtual ICollection DataSources => dataSources;
///
- public virtual void Add( Action convention ) => conventionBuilder.Add( convention );
+ public virtual void Add( Action convention ) =>
+ conventionBuilder.Add( convention );
- private sealed class ServiceProviderDecorator : IServiceProvider
+ private sealed class ServiceProviderDecorator(
+ IServiceProvider decorated,
+ ApiVersionSetBuilder versionSetBuilder ) : IServiceProvider
{
- private readonly IServiceProvider decorated;
- private readonly ApiVersionSetBuilder versionSetBuilder;
private ApiVersionSet? versionSet;
- internal ServiceProviderDecorator( IServiceProvider decorated, ApiVersionSetBuilder versionSetBuilder )
- {
- this.decorated = decorated;
- this.versionSetBuilder = versionSetBuilder;
- }
-
public object? GetService( Type serviceType )
{
+ if ( typeof( ApiVersionSetBuilder ).Equals( serviceType ) )
+ {
+ return versionSetBuilder;
+ }
+
if ( typeof( ApiVersionSet ).Equals( serviceType ) )
{
return versionSet ??= versionSetBuilder.Build();
@@ -80,17 +81,10 @@ internal ServiceProviderDecorator( IServiceProvider decorated, ApiVersionSetBuil
}
}
- private sealed class EndpointDataSourceDecorator : EndpointDataSource
+ private sealed class EndpointDataSourceDecorator(
+ EndpointDataSource decorated,
+ ApiVersionSetBuilder versionSetBuilder ) : EndpointDataSource
{
- private readonly EndpointDataSource decorated;
- private readonly ApiVersionSetBuilder versionSetBuilder;
-
- internal EndpointDataSourceDecorator( EndpointDataSource decorated, ApiVersionSetBuilder versionSetBuilder )
- {
- this.decorated = decorated;
- this.versionSetBuilder = versionSetBuilder;
- }
-
public override IReadOnlyList Endpoints => decorated.Endpoints;
public override IChangeToken GetChangeToken() => decorated.GetChangeToken();
@@ -99,14 +93,16 @@ public override IReadOnlyList GetGroupedEndpoints( RouteGroupContext c
{
CollateGroupApiVersions();
- // HACK: we don't have a way to pass the version set for the group down to each convention so
- // decorate the service provider to allow it to be resolved. this requires rebuilding the
- // current context as well.
+ // HACK: we don't have a way to pass the version set for the group down
+ // to each convention so decorate the service provider to allow it to
+ // be resolved. this requires rebuilding the current context as well.
if ( context.ApplicationServices is not ServiceProviderDecorator )
{
context = new()
{
- ApplicationServices = new ServiceProviderDecorator( context.ApplicationServices, versionSetBuilder ),
+ ApplicationServices = new ServiceProviderDecorator(
+ context.ApplicationServices,
+ versionSetBuilder ),
Conventions = context.Conventions,
FinallyConventions = context.FinallyConventions,
Prefix = context.Prefix,
@@ -116,7 +112,8 @@ public override IReadOnlyList GetGroupedEndpoints( RouteGroupContext c
return decorated.GetGroupedEndpoints( context );
}
- public override bool Equals( object? obj ) => ReferenceEquals( this, obj ) || ReferenceEquals( decorated, obj );
+ public override bool Equals( object? obj ) =>
+ ReferenceEquals( this, obj ) || ReferenceEquals( decorated, obj );
public override int GetHashCode() => decorated.GetHashCode();
@@ -152,7 +149,7 @@ private void CollateGroupApiVersions()
for ( var k = 0; k < versions.Count; k++ )
{
- add( versions[i] );
+ add( versions[k] );
}
}
}
@@ -161,19 +158,10 @@ private void CollateGroupApiVersions()
}
}
- private sealed class EndpointDataSourceCollectionAdapter : ICollection
- {
- private readonly ICollection