Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion ApiVersioningWithSamples.sln
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Versioning", "Versioning",
src\Common\Versioning\ConstantApiVersionSelector.cs = src\Common\Versioning\ConstantApiVersionSelector.cs
src\Common\Versioning\CurrentImplementationApiVersionSelector.cs = src\Common\Versioning\CurrentImplementationApiVersionSelector.cs
src\Common\Versioning\DefaultApiVersionSelector.cs = src\Common\Versioning\DefaultApiVersionSelector.cs
src\Common\Versioning\ErrorResponseContext.cs = src\Common\Versioning\ErrorResponseContext.cs
src\Common\Versioning\HeaderApiVersionReader.cs = src\Common\Versioning\HeaderApiVersionReader.cs
src\Common\Versioning\IApiVersionNeutral.cs = src\Common\Versioning\IApiVersionNeutral.cs
src\Common\Versioning\IApiVersionProvider.cs = src\Common\Versioning\IApiVersionProvider.cs
src\Common\Versioning\IApiVersionReader.cs = src\Common\Versioning\IApiVersionReader.cs
src\Common\Versioning\IApiVersionSelector.cs = src\Common\Versioning\IApiVersionSelector.cs
src\Common\Versioning\IErrorResponseProvider.cs = src\Common\Versioning\IErrorResponseProvider.cs
src\Common\Versioning\LowestImplementedApiVersionSelector.cs = src\Common\Versioning\LowestImplementedApiVersionSelector.cs
src\Common\Versioning\QueryStringApiVersionReader.cs = src\Common\Versioning\QueryStringApiVersionReader.cs
src\Common\Versioning\QueryStringOrHeaderApiVersionReader.cs = src\Common\Versioning\QueryStringOrHeaderApiVersionReader.cs
src\Common\Versioning\UrlSegmentApiVersionReader.cs = src\Common\Versioning\UrlSegmentApiVersionReader.cs
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{915BB224-B1D0-4E27-A348-67FCC77AAA44}"
Expand Down
7 changes: 3 additions & 4 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)
MIT License

Copyright (c) 2015 Commonsense Software
Copyright (c) Microsoft Corporation. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand All @@ -18,5 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

SOFTWARE
2 changes: 1 addition & 1 deletion samples/aspnetcore/BasicSample/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "1.0.0",
"type": "platform"
},
"Microsoft.AspNetCore.Mvc": "1.0.0",
"Microsoft.AspNetCore.Mvc": "1.1.1",
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
Expand Down
2 changes: 1 addition & 1 deletion samples/aspnetcore/ConventionsSample/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "1.0.0",
"type": "platform"
},
"Microsoft.AspNetCore.Mvc": "1.0.0",
"Microsoft.AspNetCore.Mvc": "1.1.1",
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
Expand Down
11 changes: 3 additions & 8 deletions samples/webapi/AdvancedODataWebApiSample/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,9 @@ public void Configuration( IAppBuilder appBuilder )
{
o.ReportApiVersions = true;
o.AssumeDefaultVersionWhenUnspecified = true;
o.ApiVersionReader = new QueryStringOrHeaderApiVersionReader()
{
HeaderNames =
{
"api-version",
"x-ms-version"
}
};
o.ApiVersionReader = ApiVersionReader.Combine(
new QueryStringApiVersionReader(),
new HeaderApiVersionReader( "api-version", "x-ms-version" ) );
} );
configuration.EnableCaseInsensitive( true );
configuration.EnableUnqualifiedNameCall( true );
Expand Down
89 changes: 84 additions & 5 deletions src/Common/Versioning/ApiVersionReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,98 @@ namespace Microsoft.Web.Http.Versioning
namespace Microsoft.AspNetCore.Mvc.Versioning
#endif
{
#if !WEBAPI
using Http;
#endif
using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
#if WEBAPI
using HttpRequest = System.Net.Http.HttpRequestMessage;
#endif
using static System.String;

/// <summary>
/// Represents the base implementation of a service API version reader.
/// Provides utility functions for service API version readers.
/// </summary>
public abstract partial class ApiVersionReader : IApiVersionReader
public static class ApiVersionReader
{
/// <summary>
/// Initializes a new instance of the <see cref="ApiVersionReader"/> class.
/// Returns a new API version reader that is a combination of the specified set.
/// </summary>
protected ApiVersionReader()
/// <param name="apiVersionReaders">The <see cref="Array">array</see> of
/// <see cref="IApiVersionReader">API version readers</see> to combine.</param>
/// <returns>A new, unioned <see cref="IApiVersionReader">API version reader</see>.</returns>
#if !WEBAPI
[CLSCompliant( false )]
#endif
public static IApiVersionReader Combine( params IApiVersionReader[] apiVersionReaders )
{
Arg.NotNull( apiVersionReaders, nameof( apiVersionReaders ) );
Contract.Ensures( Contract.Result<IApiVersionReader>() != null );
Contract.EndContractBlock();

if ( apiVersionReaders.Length == 0 )
{
throw new ArgumentException( SR.ZeroApiVersionReaders, nameof( apiVersionReaders ) );
}

return new CombinedApiVersionReader( apiVersionReaders );
}

/// <summary>
/// Returns a new API version reader that is a combination of the specified set.
/// </summary>
/// <param name="apiVersionReaders">The <see cref="IEnumerable{T}">sequence</see> of
/// <see cref="IApiVersionReader">API version readers</see> to combine.</param>
/// <returns>A new, unioned <see cref="IApiVersionReader">API version reader</see>.</returns>
#if !WEBAPI
[CLSCompliant( false )]
#endif
public static IApiVersionReader Combine( IEnumerable<IApiVersionReader> apiVersionReaders )
{
Arg.NotNull( apiVersionReaders, nameof( apiVersionReaders ) );
Contract.Ensures( Contract.Result<IApiVersionReader>() != null );
Contract.EndContractBlock();

var items = apiVersionReaders.ToArray();

if ( items.Length == 0 )
{
throw new ArgumentException( SR.ZeroApiVersionReaders, nameof( apiVersionReaders ) );
}

return new CombinedApiVersionReader( items );
}

sealed class CombinedApiVersionReader : IApiVersionReader
{
readonly IApiVersionReader[] apiVersionReaders;

internal CombinedApiVersionReader( IApiVersionReader[] apiVersionReaders )
{
Contract.Requires( apiVersionReaders != null );
Contract.Requires( apiVersionReaders.Length > 0 );
this.apiVersionReaders = apiVersionReaders;
}

public string Read( HttpRequest request )
{
var versions = new HashSet<string>( StringComparer.OrdinalIgnoreCase );

foreach ( var apiVersionReader in apiVersionReaders )
{
var version = apiVersionReader.Read( request );

if ( !IsNullOrEmpty( version ) )
{
versions.Add( version );
}
}

return versions.EnsureZeroOrOneApiVersions();
}
}
}
}
}
34 changes: 31 additions & 3 deletions src/Common/Versioning/ApiVersioningOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ namespace Microsoft.AspNetCore.Mvc.Versioning
using Conventions;
using System;
using System.Diagnostics.Contracts;
#if WEBAPI
using static Microsoft.Web.Http.Versioning.ApiVersionReader;
#else
using static Microsoft.AspNetCore.Mvc.Versioning.ApiVersionReader;
#endif

/// <summary>
/// Represents the possible API versioning options for services.
/// </summary>
public partial class ApiVersioningOptions
{
private ApiVersion defaultApiVersion = ApiVersion.Default;
private IApiVersionReader apiVersionReader = new QueryStringApiVersionReader();
private IApiVersionReader apiVersionReader = Combine( new QueryStringApiVersionReader(), new UrlSegmentApiVersionReader() );
private IApiVersionSelector apiVersionSelector;
private IErrorResponseProvider errorResponseProvider = new DefaultErrorResponseProvider();
private ApiVersionConventionBuilder conventions = new ApiVersionConventionBuilder();

/// <summary>
Expand Down Expand Up @@ -80,7 +86,7 @@ public ApiVersion DefaultApiVersion
/// service API version specified by a client. The default value is the
/// <see cref="QueryStringApiVersionReader"/>, which only reads the service API version from
/// the "api-version" query string parameter. Replace the default value with an alternate
/// implementation, such as the <see cref="QueryStringOrHeaderApiVersionReader"/>, which
/// implementation, such as the <see cref="HeaderApiVersionReader"/>, which
/// can read the service API version from additional information like HTTP headers.</remarks>
#if !WEBAPI
[CLSCompliant( false )]
Expand Down Expand Up @@ -140,9 +146,31 @@ public ApiVersionConventionBuilder Conventions
}
set
{
Arg.NotNull( conventions, nameof( conventions ) );
Arg.NotNull( value, nameof( value ) );
conventions = value;
}
}

/// <summary>
/// Gets or sets the object used to generate HTTP error responses related to API versioning.
/// </summary>
/// <value>An <see cref="IErrorResponseProvider">error response provider</see> object.
/// The default value is an instance of the <see cref="DefaultErrorResponseProvider"/>.</value>
#if !WEBAPI
[CLSCompliant( false )]
#endif
public IErrorResponseProvider ErrorResponses
{
get
{
Contract.Ensures( errorResponseProvider != null );
return errorResponseProvider;
}
set
{
Arg.NotNull( value, nameof( value ) );
errorResponseProvider = value;
}
}
}
}
30 changes: 30 additions & 0 deletions src/Common/Versioning/ErrorResponseContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#if WEBAPI
namespace Microsoft.Web.Http.Versioning
#else
namespace Microsoft.AspNetCore.Mvc.Versioning
#endif
{
/// <summary>
/// Represents the contextual information used when generating HTTP error responses related to API versioning.
/// </summary>
public partial class ErrorResponseContext
{
/// <summary>
/// Gets the associated error code.
/// </summary>
/// <value>The associated error code.</value>
public string Code { get; }

/// <summary>
/// Gets the associated error message.
/// </summary>
/// <value>The error message.</value>
public string Message { get; }

/// <summary>
/// Gets the detailed error message.
/// </summary>
/// <value>The detailed error message, if any.</value>
public string MessageDetail { get; }
}
}
6 changes: 2 additions & 4 deletions src/Common/Versioning/HeaderApiVersionReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@ namespace Microsoft.AspNetCore.Mvc.Versioning
/// <summary>
/// Represents a service API version reader that reads the value from a HTTP header.
/// </summary>
public partial class HeaderApiVersionReader : ApiVersionReader
public partial class HeaderApiVersionReader : IApiVersionReader
{
/// <summary>
/// Initializes a new instance of the <see cref="HeaderApiVersionReader"/> class.
/// </summary>
public HeaderApiVersionReader()
{
}
public HeaderApiVersionReader() { }

/// <summary>
/// Initializes a new instance of the <see cref="HeaderApiVersionReader"/> class.
Expand Down
36 changes: 36 additions & 0 deletions src/Common/Versioning/IErrorResponseProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#if WEBAPI
namespace Microsoft.Web.Http.Versioning
#else
namespace Microsoft.AspNetCore.Mvc.Versioning
#endif
{
#if WEBAPI
using IActionResult = System.Net.Http.HttpResponseMessage;
#else
using Http;
#endif
using System;

/// <summary>
/// Defines the behavior of an object that provides HTTP error responses related to API versioning.
/// </summary>
#if !WEBAPI
[CLSCompliant( false )]
#endif
public interface IErrorResponseProvider
{
/// <summary>
/// Creates and returns a new HTTP 400 (Bad Request) given the provided context.
/// </summary>
/// <param name="context">The <see cref="ErrorResponseContext">error context</see> used to generate response.</param>
/// <returns>The generated <see cref="IActionResult">response</see>.</returns>
IActionResult BadRequest( ErrorResponseContext context );

/// <summary>
/// Creates and returns a new HTTP 405 (Method Not Allowed) given the provided context.
/// </summary>
/// <param name="context">The <see cref="ErrorResponseContext">error context</see> used to generate response.</param>
/// <returns>The generated <see cref="IActionResult">response</see>.</returns>
IActionResult MethodNotAllowed( ErrorResponseContext context );
}
}
30 changes: 21 additions & 9 deletions src/Common/Versioning/QueryStringApiVersionReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ namespace Microsoft.Web.Http.Versioning
namespace Microsoft.AspNetCore.Mvc.Versioning
#endif
{
using Routing;
using System;
using System.Linq;
using System.Diagnostics.Contracts;

/// <summary>
/// Represents a service API version reader that reads the value from the query string in a URL.
/// </summary>
public partial class QueryStringApiVersionReader : ApiVersionReader
public partial class QueryStringApiVersionReader : IApiVersionReader
{
string parameterName = "api-version";

/// <summary>
/// Initializes a new instance of the <see cref="QueryStringApiVersionReader"/> class.
/// </summary>
public QueryStringApiVersionReader()
{
ParameterName = "api-version";
}
public QueryStringApiVersionReader() { }

/// <summary>
/// Initializes a new instance of the <see cref="QueryStringApiVersionReader"/> class.
Expand All @@ -31,10 +31,22 @@ public QueryStringApiVersionReader( string parameterName )
}

/// <summary>
/// Gets the name of the query parameter to read the service API version from.
/// Gets or sets the name of the query parameter to read the service API version from.
/// </summary>
/// <value>The name of the query parameter to read the service API version from.
/// The default value is "api-version".</value>
protected string ParameterName { get; }
public string ParameterName
{
get
{
Contract.Ensures( !string.IsNullOrEmpty( parameterName ) );
return parameterName;
}
set
{
Arg.NotNullOrEmpty( value, nameof( value ) );
parameterName = value;
}
}
}
}
}
Loading