зеркало из https://github.com/aspnet/Mvc.git
[Fixes #1155] Added functional tests to cover common versioning scenarios.
This commit is contained in:
Родитель
78a4e78358
Коммит
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}"
|
||||
EndProject
|
||||
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
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ReflectedModelWebSite", "test\WebSites\ReflectedModelWebSite\ReflectedModelWebSite.kproj", "{C2EF54F8-8886-4260-A322-44F76245F95D}"
|
||||
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.Build.0 = 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.Build.0 = 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}
|
||||
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3} = {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}
|
||||
{2B2B9876-903C-4065-8D62-2EE832BBA106} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||
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": "",
|
||||
"FormatterWebSite": "",
|
||||
"InlineConstraintsWebSite": "",
|
||||
"VersioningWebSite": "",
|
||||
"Microsoft.AspNet.TestHost": "1.0.0-*",
|
||||
"Microsoft.AspNet.PipelineCore": "1.0.0-*",
|
||||
"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": { }
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче