11namespace Microsoft . Web . OData . Controllers
22{
33 using Http ;
4+ using Http . Versioning ;
45 using System ;
6+ using System . Collections . Generic ;
7+ using System . Diagnostics . Contracts ;
8+ using System . Linq ;
9+ using System . Net . Http ;
10+ using System . Net . Http . Headers ;
511 using System . Web . Http ;
12+ using System . Web . Http . Controllers ;
613 using System . Web . OData ;
14+ using static Microsoft . OData . Core . ODataConstants ;
15+ using static Microsoft . OData . Core . ODataUtils ;
16+ using static Microsoft . OData . Core . ODataVersion ;
17+ using static System . Net . HttpStatusCode ;
18+ using static System . String ;
719
820 /// <summary>
921 /// Represents a <see cref="ApiController">controller</see> for generating versioned OData service and metadata documents.
1224 [ ApiVersionNeutral ]
1325 public class VersionedMetadataController : MetadataController
1426 {
27+ private sealed class DiscoveredApiVersions
28+ {
29+ private const string ValueSeparator = ", " ;
30+
31+ internal DiscoveredApiVersions ( IEnumerable < ApiVersionModel > models )
32+ {
33+ Contract . Requires ( models != null ) ;
34+
35+ var supported = new HashSet < ApiVersion > ( ) ;
36+ var deprecated = new HashSet < ApiVersion > ( ) ;
37+
38+ foreach ( var model in models )
39+ {
40+ foreach ( var version in model . SupportedApiVersions )
41+ {
42+ supported . Add ( version ) ;
43+ }
44+
45+ foreach ( var version in model . DeprecatedApiVersions )
46+ {
47+ deprecated . Add ( version ) ;
48+ }
49+ }
50+
51+ if ( supported . Count > 0 )
52+ {
53+ deprecated . ExceptWith ( supported ) ;
54+ SupportedApiVersions = Join ( ValueSeparator , supported . OrderBy ( v => v ) . Select ( v => v . ToString ( ) ) ) ;
55+ }
56+
57+ if ( deprecated . Count > 0 )
58+ {
59+ DeprecatedApiVersions = Join ( ValueSeparator , deprecated . OrderBy ( v => v ) . Select ( v => v . ToString ( ) ) ) ;
60+ }
61+ }
62+
63+ public string SupportedApiVersions { get ; }
64+
65+ public string DeprecatedApiVersions { get ; }
66+ }
67+
68+ private const string ApiSupportedVersions = "api-supported-versions" ;
69+ private const string ApiDeprecatedVersions = "api-deprecated-versions" ;
70+ private readonly Lazy < DiscoveredApiVersions > discovered ;
71+
72+ /// <summary>
73+ /// Initializes a new instance of the <see cref="VersionedMetadataController"/> class.
74+ /// </summary>
75+ public VersionedMetadataController ( )
76+ {
77+ discovered = new Lazy < DiscoveredApiVersions > ( ( ) => new DiscoveredApiVersions ( DiscoverODataApiVersions ( ) ) ) ;
78+ }
79+
80+ /// <summary>
81+ /// Handles a request for the HTTP OPTIONS method.
82+ /// </summary>
83+ /// <returns>A <see cref="IHttpActionResult">result</see> containing the response to the request.</returns>
84+ /// <remarks>When a request is made with OPTIONS /$metadata, then this method will return the following
85+ /// HTTP headers:
86+ /// <list type="table">
87+ /// <listheader>
88+ /// <term>Header Name</term>
89+ /// <description>Description</description>
90+ /// </listheader>
91+ /// <item>
92+ /// <term>OData-Version</term>
93+ /// <description>The OData version supported by the endpoint.</description>
94+ /// </item>
95+ /// <item>
96+ /// <term>api-supported-versions</term>
97+ /// <description>A comma-separated list of all supported API versions, if any.</description>
98+ /// </item>
99+ /// <item>
100+ /// <term>api-deprecated-versions</term>
101+ /// <description>A comma-separated list of all supported API versions, if any.</description>
102+ /// </item>
103+ /// </list>
104+ /// </remarks>
105+ [ HttpOptions ]
106+ public virtual IHttpActionResult GetOptions ( )
107+ {
108+ var response = new HttpResponseMessage ( OK ) ;
109+ var headers = response . Headers ;
110+
111+ response . Content = new StringContent ( Empty ) ;
112+ response . Content . Headers . Add ( "Allow" , new [ ] { "GET" , "OPTIONS" } ) ;
113+ response . Content . Headers . ContentType = null ;
114+ headers . Add ( ODataVersionHeader , ODataVersionToString ( V4 ) ) ;
115+ ReportApiVersions ( headers ) ;
116+
117+ return ResponseMessage ( response ) ;
118+ }
119+
120+ private DiscoveredApiVersions Discovered => discovered . Value ;
121+
122+ private void ReportApiVersions ( HttpHeaders headers )
123+ {
124+ Contract . Requires ( headers != null ) ;
125+
126+ var value = Discovered . SupportedApiVersions ;
127+
128+ if ( value != null )
129+ {
130+ headers . Add ( ApiSupportedVersions , value ) ;
131+ }
132+
133+ value = Discovered . DeprecatedApiVersions ;
134+
135+ if ( value != null )
136+ {
137+ headers . Add ( ApiDeprecatedVersions , value ) ;
138+ }
139+ }
140+
141+ private IEnumerable < ApiVersionModel > DiscoverODataApiVersions ( )
142+ {
143+ Contract . Ensures ( Contract . Result < IEnumerable < ApiVersionModel > > ( ) != null ) ;
144+
145+ var services = Configuration . Services ;
146+ var assembliesResolver = services . GetAssembliesResolver ( ) ;
147+ var typeResolver = services . GetHttpControllerTypeResolver ( ) ;
148+ var controllerTypes = typeResolver . GetControllerTypes ( assembliesResolver ) ;
149+
150+ return from controllerType in controllerTypes
151+ where controllerType . IsODataController ( )
152+ let descriptor = new HttpControllerDescriptor ( Configuration , string . Empty , controllerType )
153+ select descriptor . GetApiVersionModel ( ) ;
154+ }
15155 }
16- }
156+ }
0 commit comments