-
Notifications
You must be signed in to change notification settings - Fork 707
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add complex media type API version reader support. Resolves #887
- Loading branch information
1 parent
d4b6cfc
commit 5250465
Showing
9 changed files
with
1,497 additions
and
0 deletions.
There are no files selected for viewing
123 changes: 123 additions & 0 deletions
123
src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
|
||
namespace Asp.Versioning; | ||
|
||
using Asp.Versioning.Routing; | ||
using System.Globalization; | ||
using System.Net.Http.Headers; | ||
using System.Web.Http.Routing; | ||
|
||
/// <content> | ||
/// Provides additional implementation specific to ASP.NET Web API. | ||
/// </content> | ||
public partial class MediaTypeApiVersionReaderBuilder | ||
{ | ||
/// <summary> | ||
/// Adds a template used to read an API version from a media type. | ||
/// </summary> | ||
/// <param name="template">The template used to match the media type.</param> | ||
/// <param name="parameterName">The optional name of the API version parameter in the template. | ||
/// If a value is not specified, there is expected to be a single template parameter.</param> | ||
/// <returns>The current <see cref="MediaTypeApiVersionReaderBuilder"/>.</returns> | ||
/// <remarks>The template syntax is the same used by route templates; however, constraints are not supported.</remarks> | ||
#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 ) ); | ||
} | ||
|
||
if ( string.IsNullOrEmpty( parameterName ) ) | ||
{ | ||
var parser = new RouteParser(); | ||
var parsedRoute = parser.Parse( template ); | ||
var parameters = from content in parsedRoute.PathSegments.OfType<IPathContentSegment>() | ||
from parameter in content.Subsegments.OfType<IPathParameterSubsegment>() | ||
select parameter; | ||
|
||
if ( parameters.Count() > 1 ) | ||
{ | ||
var message = string.Format( CultureInfo.CurrentCulture, CommonSR.InvalidMediaTypeTemplate, template ); | ||
throw new ArgumentException( message, nameof( template ) ); | ||
} | ||
} | ||
|
||
var route = new HttpRoute( template ); | ||
|
||
AddReader( mediaTypes => ReadMediaTypePattern( mediaTypes, route, parameterName ) ); | ||
|
||
return this; | ||
} | ||
|
||
private static IReadOnlyList<string> ReadMediaTypePattern( | ||
IReadOnlyList<MediaTypeHeaderValue> mediaTypes, | ||
HttpRoute route, | ||
string? parameterName ) | ||
{ | ||
var assumeOneParameter = string.IsNullOrEmpty( parameterName ); | ||
var version = default( string ); | ||
var versions = default( List<string> ); | ||
using var request = new HttpRequestMessage(); | ||
|
||
for ( var i = 0; i < mediaTypes.Count; i++ ) | ||
{ | ||
var mediaType = mediaTypes[i].MediaType; | ||
request.RequestUri = new Uri( "http://localhost/" + mediaType ); | ||
var data = route.GetRouteData( string.Empty, request ); | ||
|
||
if ( data == null ) | ||
{ | ||
continue; | ||
} | ||
|
||
var values = data.Values; | ||
|
||
if ( values.Count == 0 ) | ||
{ | ||
continue; | ||
} | ||
|
||
object datum; | ||
|
||
if ( assumeOneParameter ) | ||
{ | ||
datum = values.Values.First(); | ||
} | ||
else if ( !values.TryGetValue( parameterName, out datum ) ) | ||
{ | ||
continue; | ||
} | ||
|
||
if ( datum is not string value || string.IsNullOrEmpty( value ) ) | ||
{ | ||
continue; | ||
} | ||
|
||
if ( version == null ) | ||
{ | ||
version = value; | ||
} | ||
else if ( versions == null ) | ||
{ | ||
versions = new( capacity: mediaTypes.Count - i + 1 ) | ||
{ | ||
version, | ||
value, | ||
}; | ||
} | ||
else | ||
{ | ||
versions.Add( value ); | ||
} | ||
} | ||
|
||
if ( version is null ) | ||
{ | ||
return Array.Empty<string>(); | ||
} | ||
|
||
return versions is null ? new[] { version } : versions.ToArray(); | ||
} | ||
} |
Oops, something went wrong.