1
+ // Copyright (c) .NET Foundation and contributors. All rights reserved.
2
+
3
+ namespace Asp . Versioning ;
4
+
5
+ using Asp . Versioning . Routing ;
6
+ using System . Globalization ;
7
+ using System . Net . Http . Headers ;
8
+ using System . Web . Http . Routing ;
9
+
10
+ /// <content>
11
+ /// Provides additional implementation specific to ASP.NET Web API.
12
+ /// </content>
13
+ public partial class MediaTypeApiVersionReaderBuilder
14
+ {
15
+ /// <summary>
16
+ /// Adds a template used to read an API version from a media type.
17
+ /// </summary>
18
+ /// <param name="template">The template used to match the media type.</param>
19
+ /// <param name="parameterName">The optional name of the API version parameter in the template.
20
+ /// If a value is not specified, there is expected to be a single template parameter.</param>
21
+ /// <returns>The current <see cref="MediaTypeApiVersionReaderBuilder"/>.</returns>
22
+ /// <remarks>The template syntax is the same used by route templates; however, constraints are not supported.</remarks>
23
+ #pragma warning disable CA1716 // Identifiers should not match keywords
24
+ public virtual MediaTypeApiVersionReaderBuilder Template ( string template , string ? parameterName = default )
25
+ #pragma warning restore CA1716 // Identifiers should not match keywords
26
+ {
27
+ if ( string . IsNullOrEmpty ( template ) )
28
+ {
29
+ throw new ArgumentNullException ( nameof ( template ) ) ;
30
+ }
31
+
32
+ if ( string . IsNullOrEmpty ( parameterName ) )
33
+ {
34
+ var parser = new RouteParser ( ) ;
35
+ var parsedRoute = parser . Parse ( template ) ;
36
+ var parameters = from content in parsedRoute . PathSegments . OfType < IPathContentSegment > ( )
37
+ from parameter in content . Subsegments . OfType < IPathParameterSubsegment > ( )
38
+ select parameter ;
39
+
40
+ if ( parameters . Count ( ) > 1 )
41
+ {
42
+ var message = string . Format ( CultureInfo . CurrentCulture , CommonSR . InvalidMediaTypeTemplate , template ) ;
43
+ throw new ArgumentException ( message , nameof ( template ) ) ;
44
+ }
45
+ }
46
+
47
+ var route = new HttpRoute ( template ) ;
48
+
49
+ AddReader ( mediaTypes => ReadMediaTypePattern ( mediaTypes , route , parameterName ) ) ;
50
+
51
+ return this ;
52
+ }
53
+
54
+ private static IReadOnlyList < string > ReadMediaTypePattern (
55
+ IReadOnlyList < MediaTypeHeaderValue > mediaTypes ,
56
+ HttpRoute route ,
57
+ string ? parameterName )
58
+ {
59
+ var assumeOneParameter = string . IsNullOrEmpty ( parameterName ) ;
60
+ var version = default ( string ) ;
61
+ var versions = default ( List < string > ) ;
62
+ using var request = new HttpRequestMessage ( ) ;
63
+
64
+ for ( var i = 0 ; i < mediaTypes . Count ; i ++ )
65
+ {
66
+ var mediaType = mediaTypes [ i ] . MediaType ;
67
+ request . RequestUri = new Uri ( "http://localhost/" + mediaType ) ;
68
+ var data = route . GetRouteData ( string . Empty , request ) ;
69
+
70
+ if ( data == null )
71
+ {
72
+ continue ;
73
+ }
74
+
75
+ var values = data . Values ;
76
+
77
+ if ( values . Count == 0 )
78
+ {
79
+ continue ;
80
+ }
81
+
82
+ object datum ;
83
+
84
+ if ( assumeOneParameter )
85
+ {
86
+ datum = values . Values . First ( ) ;
87
+ }
88
+ else if ( ! values . TryGetValue ( parameterName , out datum ) )
89
+ {
90
+ continue ;
91
+ }
92
+
93
+ if ( datum is not string value || string . IsNullOrEmpty ( value ) )
94
+ {
95
+ continue ;
96
+ }
97
+
98
+ if ( version == null )
99
+ {
100
+ version = value ;
101
+ }
102
+ else if ( versions == null )
103
+ {
104
+ versions = new ( capacity : mediaTypes . Count - i + 1 )
105
+ {
106
+ version ,
107
+ value ,
108
+ } ;
109
+ }
110
+ else
111
+ {
112
+ versions . Add ( value ) ;
113
+ }
114
+ }
115
+
116
+ if ( version is null )
117
+ {
118
+ return Array . Empty < string > ( ) ;
119
+ }
120
+
121
+ return versions is null ? new [ ] { version } : versions . ToArray ( ) ;
122
+ }
123
+ }
0 commit comments