[Fixes #1155] Added functional tests to cover common versioning scenarios.
This commit is contained in:
parent
78a4e78358
commit
8802c831a0
12
Mvc.sln
12
Mvc.sln
|
|
@ -81,6 +81,7 @@ EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "UrlHelperWebSite", "test\WebSites\UrlHelperWebSite\UrlHelperWebSite.kproj", "{A192E504-2881-41DC-90D1-B7F1DD1134E8}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "UrlHelperWebSite", "test\WebSites\UrlHelperWebSite\UrlHelperWebSite.kproj", "{A192E504-2881-41DC-90D1-B7F1DD1134E8}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ApiExplorerWebSite", "test\WebSites\ApiExplorerWebSite\ApiExplorerWebSite.kproj", "{61061528-071E-424E-965A-07BCC2F02672}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ApiExplorerWebSite", "test\WebSites\ApiExplorerWebSite\ApiExplorerWebSite.kproj", "{61061528-071E-424E-965A-07BCC2F02672}"
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "VersioningWebSite", "test\WebSites\VersioningWebSite\VersioningWebSite.kproj", "{C6304029-78C8-4604-99BE-2078DCA1DD36}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ReflectedModelWebSite", "test\WebSites\ReflectedModelWebSite\ReflectedModelWebSite.kproj", "{C2EF54F8-8886-4260-A322-44F76245F95D}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ReflectedModelWebSite", "test\WebSites\ReflectedModelWebSite\ReflectedModelWebSite.kproj", "{C2EF54F8-8886-4260-A322-44F76245F95D}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|
@ -438,6 +439,16 @@ Global
|
||||||
{61061528-071E-424E-965A-07BCC2F02672}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
{61061528-071E-424E-965A-07BCC2F02672}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
{61061528-071E-424E-965A-07BCC2F02672}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
{61061528-071E-424E-965A-07BCC2F02672}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
{61061528-071E-424E-965A-07BCC2F02672}.Release|x86.ActiveCfg = Release|Any CPU
|
{61061528-071E-424E-965A-07BCC2F02672}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{C6304029-78C8-4604-99BE-2078DCA1DD36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C6304029-78C8-4604-99BE-2078DCA1DD36}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C6304029-78C8-4604-99BE-2078DCA1DD36}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{C6304029-78C8-4604-99BE-2078DCA1DD36}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{C6304029-78C8-4604-99BE-2078DCA1DD36}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{C6304029-78C8-4604-99BE-2078DCA1DD36}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C6304029-78C8-4604-99BE-2078DCA1DD36}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C6304029-78C8-4604-99BE-2078DCA1DD36}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{C6304029-78C8-4604-99BE-2078DCA1DD36}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{C6304029-78C8-4604-99BE-2078DCA1DD36}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{C2EF54F8-8886-4260-A322-44F76245F95D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{C2EF54F8-8886-4260-A322-44F76245F95D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{C2EF54F8-8886-4260-A322-44F76245F95D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{C2EF54F8-8886-4260-A322-44F76245F95D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{C2EF54F8-8886-4260-A322-44F76245F95D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
{C2EF54F8-8886-4260-A322-44F76245F95D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
|
@ -498,6 +509,7 @@ Global
|
||||||
{A192E504-2881-41DC-90D1-B7F1DD1134E8} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
{A192E504-2881-41DC-90D1-B7F1DD1134E8} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
{61061528-071E-424E-965A-07BCC2F02672} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
{61061528-071E-424E-965A-07BCC2F02672} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
|
{C6304029-78C8-4604-99BE-2078DCA1DD36} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
{C2EF54F8-8886-4260-A322-44F76245F95D} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
{C2EF54F8-8886-4260-A322-44F76245F95D} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
{2B2B9876-903C-4065-8D62-2EE832BBA106} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
{2B2B9876-903C-4065-8D62-2EE832BBA106} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,613 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.TestHost;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
|
{
|
||||||
|
public class VersioningTests
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _services = TestHelper.CreateServices("VersioningWebSite");
|
||||||
|
private readonly Action<IApplicationBuilder> _app = new VersioningWebSite.Startup().Configure;
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("1")]
|
||||||
|
[InlineData("2")]
|
||||||
|
public async Task AttributeRoutedAction_WithVersionedRoutes_IsNotAmbiguous(string version)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/Addresses?version=" + version);
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Contains("api/addresses", result.ExpectedUrls);
|
||||||
|
Assert.Equal("Address", result.Controller);
|
||||||
|
Assert.Equal("GetV" + version, result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("1")]
|
||||||
|
[InlineData("2")]
|
||||||
|
public async Task AttributeRoutedAction_WithAmbiguousVersionedRoutes_CanBeDisambiguatedUsingOrder(string version)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
var query = "?version=" + version;
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/Addresses/All" + query);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Contains("/api/addresses/all?version=" + version, result.ExpectedUrls);
|
||||||
|
Assert.Equal("Address", result.Controller);
|
||||||
|
Assert.Equal("GetAllV" + version, result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task VersionedApi_CanReachV1Operations_OnTheSameController_WithNoVersionSpecified()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Get, "http://localhost/Tickets");
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Tickets", result.Controller);
|
||||||
|
Assert.Equal("Get", result.Action);
|
||||||
|
|
||||||
|
Assert.DoesNotContain("id", result.RouteValues.Keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task VersionedApi_CanReachV1Operations_OnTheSameController_WithVersionSpecified()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Get, "http://localhost/Tickets?version=2");
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Tickets", result.Controller);
|
||||||
|
Assert.Equal("Get", result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task VersionedApi_CanReachV1OperationsWithParameters_OnTheSameController()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Get, "http://localhost/Tickets/5");
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Tickets", result.Controller);
|
||||||
|
Assert.Equal("GetById", result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task VersionedApi_CanReachV1OperationsWithParameters_OnTheSameController_WithVersionSpecified()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Get, "http://localhost/Tickets/5?version=2");
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Tickets", result.Controller);
|
||||||
|
Assert.Equal("GetById", result.Action);
|
||||||
|
Assert.NotEmpty(result.RouteValues);
|
||||||
|
|
||||||
|
Assert.Contains(
|
||||||
|
new KeyValuePair<string, object>("id", "5"),
|
||||||
|
result.RouteValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("2")]
|
||||||
|
[InlineData("3")]
|
||||||
|
[InlineData("4")]
|
||||||
|
public async Task VersionedApi_CanReachOtherVersionOperations_OnTheSameController(string version)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Tickets?version=" + version);
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Tickets", result.Controller);
|
||||||
|
Assert.Equal("Post", result.Action);
|
||||||
|
Assert.NotEmpty(result.RouteValues);
|
||||||
|
|
||||||
|
Assert.DoesNotContain(
|
||||||
|
new KeyValuePair<string, object>("id", "5"),
|
||||||
|
result.RouteValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task VersionedApi_CanNotReachOtherVersionOperations_OnTheSameController_WithNoVersionSpecified()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Tickets");
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsByteArrayAsync();
|
||||||
|
Assert.Empty(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("PUT", "Put", "2")]
|
||||||
|
[InlineData("PUT", "Put", "3")]
|
||||||
|
[InlineData("PUT", "Put", "4")]
|
||||||
|
[InlineData("DELETE", "Delete", "2")]
|
||||||
|
[InlineData("DELETE", "Delete", "3")]
|
||||||
|
[InlineData("DELETE", "Delete", "4")]
|
||||||
|
public async Task VersionedApi_CanReachOtherVersionOperationsWithParameters_OnTheSameController(
|
||||||
|
string method,
|
||||||
|
string action,
|
||||||
|
string version)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(new HttpMethod(method), "http://localhost/Tickets/5?version=" + version);
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Tickets", result.Controller);
|
||||||
|
Assert.Equal(action, result.Action);
|
||||||
|
Assert.NotEmpty(result.RouteValues);
|
||||||
|
|
||||||
|
Assert.Contains(
|
||||||
|
new KeyValuePair<string, object>("id", "5"),
|
||||||
|
result.RouteValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("PUT")]
|
||||||
|
[InlineData("DELETE")]
|
||||||
|
public async Task VersionedApi_CanNotReachOtherVersionOperationsWithParameters_OnTheSameController_WithNoVersionSpecified(string method)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(new HttpMethod(method), "http://localhost/Tickets/5");
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsByteArrayAsync();
|
||||||
|
Assert.Empty(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("3")]
|
||||||
|
[InlineData("4")]
|
||||||
|
[InlineData("5")]
|
||||||
|
public async Task VersionedApi_CanUseOrderToDisambiguate_OverlappingVersionRanges(string version)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Get, "http://localhost/Books?version=" + version);
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Books", result.Controller);
|
||||||
|
Assert.Equal("GetBreakingChange", result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("1")]
|
||||||
|
[InlineData("2")]
|
||||||
|
[InlineData("6")]
|
||||||
|
public async Task VersionedApi_OverlappingVersionRanges_FallsBackToLowerOrderAction(string version)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Get, "http://localhost/Books?version=" + version);
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Books", result.Controller);
|
||||||
|
Assert.Equal("Get", result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("GET", "Get")]
|
||||||
|
[InlineData("POST", "Post")]
|
||||||
|
public async Task VersionedApi_CanReachV1Operations_OnTheOriginalController_WithNoVersionSpecified(string method, string action)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(new HttpMethod(method), "http://localhost/Movies");
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Movies", result.Controller);
|
||||||
|
Assert.Equal(action, result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("GET", "Get")]
|
||||||
|
[InlineData("POST", "Post")]
|
||||||
|
public async Task VersionedApi_CanReachV1Operations_OnTheOriginalController_WithVersionSpecified(string method, string action)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(new HttpMethod(method), "http://localhost/Movies?version=2");
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Movies", result.Controller);
|
||||||
|
Assert.Equal(action, result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("GET", "GetById")]
|
||||||
|
[InlineData("PUT", "Put")]
|
||||||
|
[InlineData("DELETE", "Delete")]
|
||||||
|
public async Task VersionedApi_CanReachV1OperationsWithParameters_OnTheOriginalController(string method, string action)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(new HttpMethod(method), "http://localhost/Movies/5");
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Movies", result.Controller);
|
||||||
|
Assert.Equal(action, result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("GET", "GetById")]
|
||||||
|
[InlineData("DELETE", "Delete")]
|
||||||
|
public async Task VersionedApi_CanReachV1OperationsWithParameters_OnTheOriginalController_WithVersionSpecified(string method, string action)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(new HttpMethod(method), "http://localhost/Movies/5?version=2");
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Movies", result.Controller);
|
||||||
|
Assert.Equal(action, result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task VersionedApi_CanReachOtherVersionOperationsWithParameters_OnTheV2Controller()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Put, "http://localhost/Movies/5?version=2");
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("MoviesV2", result.Controller);
|
||||||
|
Assert.Equal("Put", result.Action);
|
||||||
|
Assert.NotEmpty(result.RouteValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("v1/Pets")]
|
||||||
|
[InlineData("v2/Pets")]
|
||||||
|
public async Task VersionedApi_CanHaveTwoRoutesWithVersionOnTheUrl_OnTheSameAction(string url)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Get, "http://localhost/" + url);
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Pets", result.Controller);
|
||||||
|
Assert.Equal("Get", result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("v1/Pets/5", "V1")]
|
||||||
|
[InlineData("v2/Pets/5", "V2")]
|
||||||
|
public async Task VersionedApi_CanHaveTwoRoutesWithVersionOnTheUrl_OnDifferentActions(string url, string version)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Get, "http://localhost/" + url);
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Pets", result.Controller);
|
||||||
|
Assert.Equal("Get" + version, result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("v1/Pets", "V1")]
|
||||||
|
[InlineData("v2/Pets", "V2")]
|
||||||
|
public async Task VersionedApi_CanHaveTwoRoutesWithVersionOnTheUrl_OnDifferentActions_WithInlineConstraint(string url, string version)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Post, "http://localhost/" + url);
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Pets", result.Controller);
|
||||||
|
Assert.Equal("Post" + version, result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Customers/5", "?version=1", "Get")]
|
||||||
|
[InlineData("Customers/5", "?version=2", "Get")]
|
||||||
|
[InlineData("Customers/5", "?version=3", "GetV3ToV5")]
|
||||||
|
[InlineData("Customers/5", "?version=4", "GetV3ToV5")]
|
||||||
|
[InlineData("Customers/5", "?version=5", "GetV3ToV5")]
|
||||||
|
public async Task VersionedApi_CanProvideVersioningInformation_UsingPlainActionConstraint(string url, string query, string actionName)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Get, "http://localhost/" + url + query);
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Customers", result.Controller);
|
||||||
|
Assert.Equal(actionName, result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task VersionedApi_ConstraintOrder_IsRespected()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Post, "http://localhost/" + "Customers?version=2");
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Customers", result.Controller);
|
||||||
|
Assert.Equal("AnyV2OrHigher", result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task VersionedApi_CanUseConstraintOrder_ToChangeSelectedAction()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Delete, "http://localhost/" + "Customers/5?version=2");
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Customers", result.Controller);
|
||||||
|
Assert.Equal("Delete", result.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("1")]
|
||||||
|
[InlineData("2")]
|
||||||
|
public async Task VersionedApi_MultipleVersionsUsingAttributeRouting_OnTheSameMethod(string version)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
var path = "/" + version + "/Vouchers?version=" + version;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Get, "http://localhost" + path);
|
||||||
|
var response = await client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Equal("Vouchers", result.Controller);
|
||||||
|
Assert.Equal("GetVouchersMultipleVersions", result.Action);
|
||||||
|
|
||||||
|
var actualUrl = Assert.Single(result.ExpectedUrls);
|
||||||
|
Assert.Equal(path, actualUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// See TestResponseGenerator in RoutingWebSite for the code that generates this data.
|
||||||
|
private class RoutingResult
|
||||||
|
{
|
||||||
|
public string[] ExpectedUrls { get; set; }
|
||||||
|
|
||||||
|
public string ActualUrl { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<string, object> RouteValues { get; set; }
|
||||||
|
|
||||||
|
public string RouteName { get; set; }
|
||||||
|
|
||||||
|
public string Action { get; set; }
|
||||||
|
|
||||||
|
public string Controller { get; set; }
|
||||||
|
|
||||||
|
public string Link { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
"FiltersWebSite": "",
|
"FiltersWebSite": "",
|
||||||
"FormatterWebSite": "",
|
"FormatterWebSite": "",
|
||||||
"InlineConstraintsWebSite": "",
|
"InlineConstraintsWebSite": "",
|
||||||
|
"VersioningWebSite": "",
|
||||||
"Microsoft.AspNet.TestHost": "1.0.0-*",
|
"Microsoft.AspNet.TestHost": "1.0.0-*",
|
||||||
"Microsoft.AspNet.PipelineCore": "1.0.0-*",
|
"Microsoft.AspNet.PipelineCore": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Mvc.TestConfiguration": "",
|
"Microsoft.AspNet.Mvc.TestConfiguration": "",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
// Scenario
|
||||||
|
// Same template disjoint version sets.
|
||||||
|
// Same template overlapping version sets disambiguated by order.
|
||||||
|
public class AddressController : Controller
|
||||||
|
{
|
||||||
|
private TestResponseGenerator _generator;
|
||||||
|
|
||||||
|
public AddressController(TestResponseGenerator generator)
|
||||||
|
{
|
||||||
|
_generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[VersionRoute("api/Addresses", versionRange: "[1]")]
|
||||||
|
public IActionResult GetV1()
|
||||||
|
{
|
||||||
|
return _generator.Generate("api/addresses");
|
||||||
|
}
|
||||||
|
|
||||||
|
[VersionRoute("api/Addresses", versionRange: "[2]")]
|
||||||
|
public IActionResult GetV2()
|
||||||
|
{
|
||||||
|
return _generator.Generate("api/addresses");
|
||||||
|
}
|
||||||
|
|
||||||
|
[VersionRoute("api/addresses/all", versionRange: "[1]")]
|
||||||
|
public IActionResult GetAllV1(string version)
|
||||||
|
{
|
||||||
|
return _generator.Generate(
|
||||||
|
Url.Action("GetAllV1",
|
||||||
|
new { version = version }), Url.RouteUrl(new { version = version }));
|
||||||
|
}
|
||||||
|
|
||||||
|
[VersionRoute("api/addresses/all", versionRange: "[1-2]", Order = 1)]
|
||||||
|
public IActionResult GetAllV2(string version)
|
||||||
|
{
|
||||||
|
return _generator.Generate(
|
||||||
|
Url.Action("GetAllV2", new { version = version }),
|
||||||
|
Url.RouteUrl(new { version = version }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
// Scenario
|
||||||
|
// Actions define version ranges and some
|
||||||
|
// versions overlap.
|
||||||
|
public class BooksController : Controller
|
||||||
|
{
|
||||||
|
private readonly TestResponseGenerator _generator;
|
||||||
|
|
||||||
|
public BooksController(TestResponseGenerator generator)
|
||||||
|
{
|
||||||
|
_generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[VersionGet("Books", versionRange: "[1-6]", Order = 1)]
|
||||||
|
public IActionResult Get()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[VersionGet("Books", versionRange: "[3-5]", Order = 0)]
|
||||||
|
public IActionResult GetBreakingChange()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
// Scenario:
|
||||||
|
// Version constraint provided separately from the attribute route.
|
||||||
|
[Route("Customers")]
|
||||||
|
public class CustomersController
|
||||||
|
{
|
||||||
|
private readonly TestResponseGenerator _generator;
|
||||||
|
|
||||||
|
public CustomersController(TestResponseGenerator generator)
|
||||||
|
{
|
||||||
|
_generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
[Version(MaxVersion = 2)]
|
||||||
|
public IActionResult Get(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
[Version(MinVersion = 3, MaxVersion = 5)]
|
||||||
|
public IActionResult GetV3ToV5(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Version(MinVersion = 2)]
|
||||||
|
public IActionResult AnyV2OrHigher()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public IActionResult Post()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Version(MinVersion = 2, Order = int.MaxValue)]
|
||||||
|
[Route("{id}")]
|
||||||
|
public IActionResult AnyV2OrHigherWithId()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public IActionResult Delete()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
// Scenario:
|
||||||
|
// Controller without any kind of specific version handling.
|
||||||
|
// New versions of the API will be exposed in a different controller.
|
||||||
|
[Route("Items/{id}")]
|
||||||
|
public class ItemsController : Controller
|
||||||
|
{
|
||||||
|
private readonly TestResponseGenerator _generator;
|
||||||
|
|
||||||
|
public ItemsController(TestResponseGenerator generator)
|
||||||
|
{
|
||||||
|
_generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("/Items")]
|
||||||
|
public IActionResult Get()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult Get(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("/Items")]
|
||||||
|
public IActionResult Post()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut]
|
||||||
|
public IActionResult Put(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
public IActionResult Delete(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
// This is the version 2 for an API. The version 1 is unconstrained
|
||||||
|
[VersionRoute("Items/{id}", versionRange: "2")]
|
||||||
|
public class ItemsV2Controller : Controller
|
||||||
|
{
|
||||||
|
private readonly TestResponseGenerator _generator;
|
||||||
|
|
||||||
|
public ItemsV2Controller(TestResponseGenerator generator)
|
||||||
|
{
|
||||||
|
_generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[VersionGet("/Items", versionRange: "2")]
|
||||||
|
public IActionResult Get()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult Get(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[VersionPost("/Items", versionRange: "2")]
|
||||||
|
public IActionResult Post()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut]
|
||||||
|
public IActionResult Put(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
public IActionResult Delete(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
// Scenario:
|
||||||
|
// This is a controller for the V1 (unconstrained) version
|
||||||
|
// of the service. The v2 version will be in a controller
|
||||||
|
// that contains only actions for which the api surface
|
||||||
|
// changes. Actions for which V1 and V2 have the same
|
||||||
|
// API surface.
|
||||||
|
public class MoviesController : Controller
|
||||||
|
{
|
||||||
|
private readonly TestResponseGenerator _generator;
|
||||||
|
|
||||||
|
public MoviesController(TestResponseGenerator generator)
|
||||||
|
{
|
||||||
|
_generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("Movies")]
|
||||||
|
public IActionResult Get()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("Movies/{id}")]
|
||||||
|
public IActionResult GetById(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("/Movies")]
|
||||||
|
public IActionResult Post()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("Movies/{id}")]
|
||||||
|
public IActionResult Put(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("Movies/{id}")]
|
||||||
|
public IActionResult Delete(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
public class MoviesV2Controller : Controller
|
||||||
|
{
|
||||||
|
private readonly TestResponseGenerator _generator;
|
||||||
|
|
||||||
|
public MoviesV2Controller(TestResponseGenerator generator)
|
||||||
|
{
|
||||||
|
_generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[VersionPut("Movies/{id}", versionRange: "2")]
|
||||||
|
public IActionResult Put(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
// Scenario
|
||||||
|
// The version is in the path of the URL
|
||||||
|
public class PetsController : Controller
|
||||||
|
{
|
||||||
|
private readonly TestResponseGenerator _generator;
|
||||||
|
|
||||||
|
public PetsController(TestResponseGenerator generator)
|
||||||
|
{
|
||||||
|
_generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("v1/Pets")]
|
||||||
|
[HttpGet("v2/Pets")]
|
||||||
|
public IActionResult Get()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("v1/Pets/{id}")]
|
||||||
|
public IActionResult GetV1(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("v2/Pets/{id}")]
|
||||||
|
public IActionResult GetV2(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("v1/Pets")]
|
||||||
|
public IActionResult PostV1()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("v{version:Min(2)}/Pets")]
|
||||||
|
public IActionResult PostV2()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
// Scenario
|
||||||
|
// V1 of the API is read-only and unconstrained
|
||||||
|
// V2 of the API is constrained
|
||||||
|
public class TicketsController : Controller
|
||||||
|
{
|
||||||
|
private readonly TestResponseGenerator _generator;
|
||||||
|
|
||||||
|
public TicketsController(TestResponseGenerator generator)
|
||||||
|
{
|
||||||
|
_generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("/Tickets")]
|
||||||
|
public IActionResult Get()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("/Tickets/{id}")]
|
||||||
|
public IActionResult GetById(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[VersionPost("/Tickets", versionRange: "2")]
|
||||||
|
public IActionResult Post()
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[VersionPut("/Tickets/{id}", versionRange: "2")]
|
||||||
|
public IActionResult Put(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[VersionDelete("/Tickets/{id}", versionRange: "2")]
|
||||||
|
public IActionResult Delete(int id)
|
||||||
|
{
|
||||||
|
return _generator.Generate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
public class VouchersController : Controller
|
||||||
|
{
|
||||||
|
private readonly TestResponseGenerator _generator;
|
||||||
|
|
||||||
|
public VouchersController(TestResponseGenerator generator)
|
||||||
|
{
|
||||||
|
_generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are verifying that the right constraint gets applied along the route.
|
||||||
|
[VersionGet("1/Vouchers", versionRange: "[1]", Name = "V1")]
|
||||||
|
[VersionGet("2/Vouchers", versionRange: "[2]", Name = "V2")]
|
||||||
|
public IActionResult GetVouchersMultipleVersions(string version)
|
||||||
|
{
|
||||||
|
return _generator.Generate(Url.RouteUrl("V" + version, new { version = version }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public void Configure(IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
var configuration = app.GetTestConfiguration();
|
||||||
|
|
||||||
|
app.UseServices(services =>
|
||||||
|
{
|
||||||
|
services.AddMvc(configuration);
|
||||||
|
|
||||||
|
services.AddScoped<TestResponseGenerator>();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.UseMvc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
// Generates a response based on the expected URL and action context
|
||||||
|
public class TestResponseGenerator
|
||||||
|
{
|
||||||
|
private readonly ActionContext _actionContext;
|
||||||
|
|
||||||
|
public TestResponseGenerator(IContextAccessor<ActionContext> contextAccessor)
|
||||||
|
{
|
||||||
|
_actionContext = contextAccessor.Value;
|
||||||
|
if (_actionContext == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("ActionContext should not be null here.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonResult Generate(params string[] expectedUrls)
|
||||||
|
{
|
||||||
|
var link = (string)null;
|
||||||
|
var query = _actionContext.HttpContext.Request.Query;
|
||||||
|
if (query.ContainsKey("link"))
|
||||||
|
{
|
||||||
|
var values = query
|
||||||
|
.Where(kvp => kvp.Key != "link" && kvp.Key != "link_action" && kvp.Key != "link_controller")
|
||||||
|
.ToDictionary(kvp => kvp.Key.Substring("link_".Length), kvp => (object)kvp.Value[0]);
|
||||||
|
|
||||||
|
var urlHelper = _actionContext.HttpContext.RequestServices.GetService<IUrlHelper>();
|
||||||
|
link = urlHelper.Action(query["link_action"], query["link_controller"], values);
|
||||||
|
}
|
||||||
|
|
||||||
|
var attributeRoutingInfo = _actionContext.ActionDescriptor.AttributeRouteInfo;
|
||||||
|
|
||||||
|
return new JsonResult(new
|
||||||
|
{
|
||||||
|
expectedUrls = expectedUrls,
|
||||||
|
actualUrl = _actionContext.HttpContext.Request.Path.Value,
|
||||||
|
routeName = attributeRoutingInfo == null ? null : attributeRoutingInfo.Name,
|
||||||
|
routeValues = new Dictionary<string, object>(_actionContext.RouteData.Values),
|
||||||
|
|
||||||
|
action = _actionContext.ActionDescriptor.Name,
|
||||||
|
controller = ((ReflectedActionDescriptor)_actionContext.ActionDescriptor).ControllerDescriptor.Name,
|
||||||
|
|
||||||
|
link,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
public class VersionAttribute : Attribute, IActionConstraintFactory
|
||||||
|
{
|
||||||
|
private int? _maxVersion;
|
||||||
|
private int? _minVersion;
|
||||||
|
private int? _order;
|
||||||
|
|
||||||
|
public int MinVersion
|
||||||
|
{
|
||||||
|
get { return _minVersion ?? -1; }
|
||||||
|
set { _minVersion = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int MaxVersion
|
||||||
|
{
|
||||||
|
get { return _maxVersion ?? -1; }
|
||||||
|
set { _maxVersion = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Order
|
||||||
|
{
|
||||||
|
get { return _order ?? -1; }
|
||||||
|
set { _order = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionConstraint CreateInstance(IServiceProvider services)
|
||||||
|
{
|
||||||
|
return new VersionRangeValidator(_minVersion, _maxVersion) { Order = _order ?? 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
public class VersionDeleteAttribute : VersionRoute, IActionHttpMethodProvider
|
||||||
|
{
|
||||||
|
public VersionDeleteAttribute(string template)
|
||||||
|
: base(template)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public VersionDeleteAttribute(string template, string versionRange)
|
||||||
|
: base(template, versionRange)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly IEnumerable<string> _httpMethods = new[] { "DELETE" };
|
||||||
|
|
||||||
|
public IEnumerable<string> HttpMethods
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _httpMethods;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
public class VersionGetAttribute : VersionRoute, IActionHttpMethodProvider
|
||||||
|
{
|
||||||
|
public VersionGetAttribute(string template)
|
||||||
|
: base(template)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public VersionGetAttribute(string template, string versionRange)
|
||||||
|
: base(template, versionRange)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly IEnumerable<string> _httpMethods = new[] { "GET" };
|
||||||
|
|
||||||
|
public IEnumerable<string> HttpMethods
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _httpMethods;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
public class VersionPostAttribute : VersionRoute, IActionHttpMethodProvider
|
||||||
|
{
|
||||||
|
public VersionPostAttribute(string template)
|
||||||
|
: base(template)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public VersionPostAttribute(string template, string versionRange)
|
||||||
|
: base(template, versionRange)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly IEnumerable<string> _httpMethods = new[] { "POST" };
|
||||||
|
|
||||||
|
public IEnumerable<string> HttpMethods
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _httpMethods;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
public class VersionPutAttribute : VersionRoute, IActionHttpMethodProvider
|
||||||
|
{
|
||||||
|
public VersionPutAttribute(string template)
|
||||||
|
: base(template)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public VersionPutAttribute(string template, string versionRange)
|
||||||
|
: base(template, versionRange)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly IEnumerable<string> _httpMethods = new[] { "PUT" };
|
||||||
|
|
||||||
|
public IEnumerable<string> HttpMethods
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _httpMethods;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.AspNet.Routing;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
public class VersionRangeValidator : IActionConstraint
|
||||||
|
{
|
||||||
|
private readonly int? _minVersion;
|
||||||
|
private readonly int? _maxVersion;
|
||||||
|
|
||||||
|
public int Order { get; set; }
|
||||||
|
|
||||||
|
public VersionRangeValidator(int? minVersion, int? maxVersion)
|
||||||
|
{
|
||||||
|
_minVersion = minVersion;
|
||||||
|
_maxVersion = maxVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetVersion(HttpRequest request)
|
||||||
|
{
|
||||||
|
return request.Query.Get("version");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Accept(ActionConstraintContext context)
|
||||||
|
{
|
||||||
|
int version;
|
||||||
|
if (int.TryParse(GetVersion(context.RouteContext.HttpContext.Request), out version))
|
||||||
|
{
|
||||||
|
return (_minVersion == null || _minVersion <= version) &&
|
||||||
|
(_maxVersion == null || _maxVersion >= version);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace VersioningWebSite
|
||||||
|
{
|
||||||
|
public class VersionRoute : RouteAttribute, IActionConstraintFactory
|
||||||
|
{
|
||||||
|
private readonly IActionConstraint _constraint;
|
||||||
|
|
||||||
|
// 5
|
||||||
|
// [5]
|
||||||
|
// (5)
|
||||||
|
// (5]
|
||||||
|
// [5)
|
||||||
|
// (3-5)
|
||||||
|
// (3-5]
|
||||||
|
// [3-5)
|
||||||
|
// [3-5]
|
||||||
|
// [35-56]
|
||||||
|
// Parses the above version formats and captures lb (lower bound), range, and hb (higher bound)
|
||||||
|
// We filter out (5), (5], [5) manually after we do the parsing.
|
||||||
|
private static readonly Regex _versionParser = new Regex(@"^(?<lb>[\(\[])?(?<range>\d+(-\d+)?)(?<hb>[\)\]])?$");
|
||||||
|
|
||||||
|
public VersionRoute(string template)
|
||||||
|
: base(template)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public VersionRoute(string template, string versionRange)
|
||||||
|
: base(template)
|
||||||
|
{
|
||||||
|
var constraint = CreateVersionConstraint(versionRange);
|
||||||
|
|
||||||
|
if (constraint == null)
|
||||||
|
{
|
||||||
|
var message = string.Format("Invalid version format: {0}", versionRange);
|
||||||
|
throw new ArgumentException(message, "versionRange");
|
||||||
|
}
|
||||||
|
|
||||||
|
_constraint = constraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IActionConstraint CreateVersionConstraint(string versionRange)
|
||||||
|
{
|
||||||
|
var match = _versionParser.Match(versionRange);
|
||||||
|
|
||||||
|
if (!match.Success)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lowerBound = match.Groups["lb"].Value;
|
||||||
|
var higherBound = match.Groups["hb"].Value;
|
||||||
|
var range = match.Groups["range"].Value;
|
||||||
|
|
||||||
|
var rangeValues = range.Split('-');
|
||||||
|
if (rangeValues.Length == 1)
|
||||||
|
{
|
||||||
|
return GetSingleVersionOrUnboundedHigherVersionConstraint(lowerBound, higherBound, rangeValues);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetBoundedRangeVersionConstraint(lowerBound, higherBound, rangeValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IActionConstraint GetBoundedRangeVersionConstraint(
|
||||||
|
string lowerBound,
|
||||||
|
string higherBound,
|
||||||
|
string[] rangeValues)
|
||||||
|
{
|
||||||
|
// [3-5, (3-5, 3-5], 3-5), 3-5 are not valid
|
||||||
|
if (string.IsNullOrEmpty(lowerBound) || string.IsNullOrEmpty(higherBound))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var minVersion = int.Parse(rangeValues[0]);
|
||||||
|
var maxVersion = int.Parse(rangeValues[1]);
|
||||||
|
|
||||||
|
// Adjust min version and max version if the limit is exclusive.
|
||||||
|
minVersion = lowerBound == "(" ? minVersion + 1 : minVersion;
|
||||||
|
maxVersion = higherBound == ")" ? maxVersion - 1 : maxVersion;
|
||||||
|
|
||||||
|
if (minVersion > maxVersion)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new VersionRangeValidator(minVersion, maxVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IActionConstraint GetSingleVersionOrUnboundedHigherVersionConstraint(
|
||||||
|
string lowerBound,
|
||||||
|
string higherBound,
|
||||||
|
string[] rangeValues)
|
||||||
|
{
|
||||||
|
// (5], [5), (5), [5, (5, 5], 5) are not valid
|
||||||
|
if (lowerBound == "(" || higherBound == ")" ||
|
||||||
|
(string.IsNullOrEmpty(lowerBound) ^ string.IsNullOrEmpty(higherBound)))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var version = int.Parse(rangeValues[0]);
|
||||||
|
if (!string.IsNullOrEmpty(lowerBound))
|
||||||
|
{
|
||||||
|
// [5]
|
||||||
|
return new VersionRangeValidator(version, version);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 5
|
||||||
|
return new VersionRangeValidator(version, maxVersion: null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionConstraint CreateInstance(IServiceProvider services)
|
||||||
|
{
|
||||||
|
return _constraint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">12.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>c6304029-78c8-4604-99be-2078dca1dd36</ProjectGuid>
|
||||||
|
<OutputType>Web</OutputType>
|
||||||
|
<RootNamespace>VersioningWebSite</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="$(OutputType) == 'Console'">
|
||||||
|
<DebuggerFlavor>ConsoleDebugger</DebuggerFlavor>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="$(OutputType) == 'Web'">
|
||||||
|
<DebuggerFlavor>WebDebugger</DebuggerFlavor>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'" Label="Configuration">
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<DevelopmentServerPort>22807</DevelopmentServerPort>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.AspNet.Mvc": "",
|
||||||
|
"Microsoft.AspNet.Mvc.TestConfiguration": "",
|
||||||
|
"Microsoft.AspNet.Server.IIS": "1.0.0-*"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"aspnet50": { },
|
||||||
|
"aspnetcore50": { }
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue