diff --git a/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRouting.cs b/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRouting.cs index 533f4d09bc..b7b06e570b 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRouting.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Routing/AttributeRouting.cs @@ -224,6 +224,11 @@ namespace Microsoft.AspNet.Mvc.Routing { if (parameter.InlineConstraints != null) { + if (parameter.IsOptional) + { + constraintBuilder.SetOptional(parameter.Name); + } + foreach (var inlineConstraint in parameter.InlineConstraints) { constraintBuilder.AddResolvedConstraint(parameter.Name, inlineConstraint.Constraint); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Routing/AttributeRouteTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Routing/AttributeRouteTest.cs index c606d68899..732d1c6e7e 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Routing/AttributeRouteTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Routing/AttributeRouteTest.cs @@ -198,6 +198,53 @@ namespace Microsoft.AspNet.Mvc.Routing Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]); } + [Theory] + [InlineData("template/{parameter:int}", "/template/5", true)] + [InlineData("template/{parameter:int?}", "/template/5", true)] + [InlineData("template/{parameter:int?}", "/template", true)] + [InlineData("template/{parameter:int?}", "/template/qwer", false)] + public async Task AttributeRoute_WithOptionalInlineConstraint(string template, string request, bool expectedResult) + { + // Arrange + var expectedRouteGroup = string.Format("{0}&&{1}", 0, template); + + // We need to force the creation of a closure in order to avoid an issue with Moq and Roslyn. + var numberOfCalls = 0; + Action callBack = ctx => { ctx.IsHandled = true; numberOfCalls++; }; + + var next = new Mock(); + next.Setup(r => r.RouteAsync(It.IsAny())) + .Callback(callBack) + .Returns(Task.FromResult(true)) + .Verifiable(); + + var firstRoute = CreateMatchingEntry(next.Object, template, order: 0); + + // We setup the route entries in reverse order of precedence to ensure that when we + // try to route the request, the route with a higher precedence gets tried first. + var matchingRoutes = new[] { firstRoute }; + + var linkGenerationEntries = Enumerable.Empty(); + + var route = new AttributeRoute(next.Object, matchingRoutes, linkGenerationEntries, NullLoggerFactory.Instance); + + var context = CreateRouteContext(request); + + // Act + await route.RouteAsync(context); + + // Assert + if (expectedResult) + { + Assert.True(context.IsHandled); + Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]); + } + else + { + Assert.False(context.IsHandled); + } + } + [Theory] [InlineData("template/5", "template/{parameter:int}")] [InlineData("template/5", "template/{parameter}")] @@ -246,6 +293,56 @@ namespace Microsoft.AspNet.Mvc.Routing Assert.Equal(expectedGroup, selectedGroup); } + [Theory] + [InlineData("template/{parameter:int}", "template/5", 5)] + [InlineData("template/{parameter:int?}", "template/5", 5)] + [InlineData("template/{parameter:int?}", "template", null)] + [InlineData("template/{parameter:int?}", null, "asdf")] + [InlineData("template/{parameter:alpha?}", "template/asdf", "asdf")] + [InlineData("template/{parameter:alpha?}", "template", null)] + [InlineData("template/{parameter:int:range(1,20)?}", "template", null)] + [InlineData("template/{parameter:int:range(1,20)?}", "template/5", 5)] + [InlineData("template/{parameter:int:range(1,20)?}", null, 21)] + public void AttributeRoute_GenerateLink_OptionalInlineParameter(string template, string expectedResult, object parameter) + { + // Arrange + var expectedGroup = CreateRouteGroup(0, template); + + string selectedGroup = null; + + var next = new Mock(); + next.Setup(n => n.GetVirtualPath(It.IsAny())).Callback(ctx => + { + selectedGroup = (string)ctx.ProvidedValues[AttributeRouting.RouteGroupKey]; + ctx.IsBound = true; + }) + .Returns((string)null); + + var matchingRoutes = Enumerable.Empty(); + + var entry = CreateGenerationEntry(template, requiredValues: null); + + var linkGenerationEntries = new[] { entry }; + + var route = new AttributeRoute(next.Object, matchingRoutes, linkGenerationEntries, NullLoggerFactory.Instance); + VirtualPathContext context; + + if (parameter != null) + { + context = CreateVirtualPathContext(values: null, ambientValues: new { parameter = parameter }); + } + else + { + context = CreateVirtualPathContext(values: null, ambientValues: null); + } + + // Act + string result = route.GetVirtualPath(context); + + // Assert + Assert.Equal(expectedResult, result); + } + [Theory] [InlineData("template/5", "template/{parameter:int}")] [InlineData("template/5", "template/{parameter}")] @@ -1295,6 +1392,11 @@ namespace Microsoft.AspNet.Mvc.Routing { if (parameter.InlineConstraints != null) { + if (parameter.IsOptional) + { + constraintBuilder.SetOptional(parameter.Name); + } + foreach (var constraint in parameter.InlineConstraints) { constraintBuilder.AddResolvedConstraint(parameter.Name, constraint.Constraint); diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/InlineConstraintTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/InlineConstraintTests.cs index a4500aa622..5dc8d1961f 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/InlineConstraintTests.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/InlineConstraintTests.cs @@ -2,11 +2,14 @@ // 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.Net; +using System.Net.Http; using System.Threading.Tasks; using InlineConstraints; using Microsoft.AspNet.Builder; using Microsoft.AspNet.TestHost; +using Newtonsoft.Json; using Xunit; namespace Microsoft.AspNet.Mvc.FunctionalTests @@ -40,12 +43,633 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests var client = server.CreateClient(); // Act & Assert - var ex = await Assert.ThrowsAsync(() => client.GetAsync("http://localhost/area-withoutexists/Users")); + var ex = await Assert.ThrowsAsync( + () => client.GetAsync("http://localhost/area-withoutexists/Users") + ); Assert.Equal("The view 'Index' was not found." + " The following locations were searched:\r\n/Areas/Users/Views/Home/Index.cshtml\r\n" + "/Areas/Users/Views/Shared/Index.cshtml\r\n/Views/Shared/Index.cshtml.", ex.Message); } + + [Fact] + public async Task GetProductById_IntConstraintForOptionalId_IdPresent() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetProductById/5"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + var result = await GetResponseValues(response); + Assert.Equal(result["id"], "5"); + Assert.Equal(result["controller"], "InlineConstraints_Products"); + Assert.Equal(result["action"], "GetProductById"); + } + + [Fact] + public async Task GetProductById_IntConstraintForOptionalId_NoId() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetProductById"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var result = await GetResponseValues(response); + Assert.Equal(result["controller"], "InlineConstraints_Products"); + Assert.Equal(result["action"], "GetProductById"); + } + + [Fact] + public async Task GetProductById_IntConstraintForOptionalId_NotIntId() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetProductById/asdf"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetProductByName_AlphaContraintForMandatoryName_ValidName() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetProductByName/asdf"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var result = await GetResponseValues(response); + Assert.Equal(result["name"], "asdf"); + Assert.Equal(result["controller"], "InlineConstraints_Products"); + Assert.Equal(result["action"], "GetProductByName"); + } + + [Fact] + public async Task GetProductByName_AlphaContraintForMandatoryName_NonAlphaName() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetProductByName/asd123"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetProductByName_AlphaContraintForMandatoryName_NoName() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetProductByName"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetProductByManufacturingDate_DateTimeConstraintForMandatoryDateTime_ValidDateTime() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = + await client.GetAsync(@"http://localhost/products/GetProductByManufacturingDate/2014-10-11T13:45:30"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + var result = await GetResponseValues(response); + Assert.Equal(result["dateTime"], new DateTime(2014, 10, 11, 13, 45, 30)); + Assert.Equal(result["controller"], "InlineConstraints_Products"); + Assert.Equal(result["action"], "GetProductByManufacturingDate"); + } + + [Fact] + public async Task GetProductByCategoryName_StringLengthConstraint_ForOptionalCategoryName_ValidCatName() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetProductByCategoryName/Sports"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var result = await GetResponseValues(response); + Assert.Equal(result["name"], "Sports"); + Assert.Equal(result["controller"], "InlineConstraints_Products"); + Assert.Equal(result["action"], "GetProductByCategoryName"); + } + + [Fact] + public async Task GetProductByCategoryName_StringLengthConstraint_ForOptionalCategoryName_InvalidCatName() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = + await client.GetAsync("http://localhost/products/GetProductByCategoryName/SportsSportsSportsSports"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetProductByCategoryName_StringLength1To20Constraint_ForOptionalCategoryName_NoCatName() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetProductByCategoryName"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var result = await GetResponseValues(response); + Assert.Equal(result["controller"], "InlineConstraints_Products"); + Assert.Equal(result["action"], "GetProductByCategoryName"); + } + + [Fact] + public async Task GetProductByCategoryId_Int10To100Constraint_ForMandatoryCatId_ValidId() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetProductByCategoryId/40"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var result = await GetResponseValues(response); + Assert.Equal(result["catId"], "40"); + Assert.Equal(result["controller"], "InlineConstraints_Products"); + Assert.Equal(result["action"], "GetProductByCategoryId"); + } + + [Fact] + public async Task GetProductByCategoryId_Int10To100Constraint_ForMandatoryCatId_InvalidId() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetProductByCategoryId/5"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetProductByCategoryId_Int10To100Constraint_ForMandatoryCatId_NotIntId() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetProductByCategoryId/asdf"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetProductByPrice_FloatContraintForOptionalPrice_Valid() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetProductByPrice/4023.23423"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var result = await GetResponseValues(response); + Assert.Equal(result["price"], "4023.23423"); + Assert.Equal(result["controller"], "InlineConstraints_Products"); + Assert.Equal(result["action"], "GetProductByPrice"); + } + + [Fact] + public async Task GetProductByPrice_FloatContraintForOptionalPrice_NoPrice() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetProductByPrice"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var result = await GetResponseValues(response); + Assert.Equal(result["controller"], "InlineConstraints_Products"); + Assert.Equal(result["action"], "GetProductByPrice"); + } + + [Fact] + public async Task GetProductByManufacturerId_IntMin10Constraint_ForOptionalManufacturerId_Valid() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetProductByManufacturerId/57"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var result = await GetResponseValues(response); + Assert.Equal(result["manId"], "57"); + Assert.Equal(result["controller"], "InlineConstraints_Products"); + Assert.Equal(result["action"], "GetProductByManufacturerId"); + } + + [Fact] + public async Task GetProductByManufacturerId_IntMin10Cinstraint_ForOptionalManufacturerId_NoId() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetProductByManufacturerId"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var result = await GetResponseValues(response); + Assert.Equal(result["controller"], "InlineConstraints_Products"); + Assert.Equal(result["action"], "GetProductByManufacturerId"); + } + + [Fact] + public async Task GetUserByName_RegExConstraint_ForMandatoryName_Valid() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetUserByName/abc"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var result = await GetResponseValues(response); + Assert.Equal(result["controller"], "InlineConstraints_Products"); + Assert.Equal(result["action"], "GetUserByName"); + Assert.Equal(result["name"], "abc"); + } + + [Fact] + public async Task GetUserByName_RegExConstraint_ForMandatoryName_InValid() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/products/GetUserByName/abcd"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetStoreById_GuidConstraintForOptionalId_Valid() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = + await client.GetAsync("http://localhost/Store/GetStoreById/691cf17a-791b-4af8-99fd-e739e168170f"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var result = await GetResponseValues(response); + Assert.Equal(result["id"], "691cf17a-791b-4af8-99fd-e739e168170f"); + Assert.Equal(result["controller"], "InlineConstraints_Store"); + Assert.Equal(result["action"], "GetStoreById"); + } + + [Fact] + public async Task GetStoreById_GuidConstraintForOptionalId_NoId() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/Store/GetStoreById"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var result = await GetResponseValues(response); + Assert.Equal(result["controller"], "InlineConstraints_Store"); + Assert.Equal(result["action"], "GetStoreById"); + } + + [Fact] + public async Task GetStoreById_GuidConstraintForOptionalId_NotGuidId() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/Store/GetStoreById/691cf17a-791b"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetStoreByLocation_StringLengthConstraint_AlphaConstraint_ForMandatoryLocation_Valid() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/Store/GetStoreByLocation/Bellevue"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var result = await GetResponseValues(response); + Assert.Equal(result["location"], "Bellevue"); + Assert.Equal(result["controller"], "InlineConstraints_Store"); + Assert.Equal(result["action"], "GetStoreByLocation"); + } + + [Fact] + public async Task GetStoreByLocation_StringLengthConstraint_AlphaConstraint_ForMandatoryLocation_MoreLength() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/Store/GetStoreByLocation/BellevueRedmond"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetStoreByLocation_StringLengthConstraint_AlphaConstraint_ForMandatoryLocation_LessLength() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/Store/GetStoreByLocation/Be"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetStoreByLocation_StringLengthConstraint_AlphaConstraint_ForMandatoryLocation_NoAlpha() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/Store/GetStoreByLocation/Bell124"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + public static IEnumerable QueryParameters + { + // The first four parameters are controller name, action name, parameters in the query and their values. + // These are used to generate a link, the last parameter is expected generated link + get + { + // Attribute Route, id:int? constraint + yield return new object[] + { + "InlineConstraints_Products", + "GetProductById", + "id", + "5", + "/products/GetProductById/5" + }; + + // Attribute Route, id:int? constraint + yield return new object[] + { + "InlineConstraints_Products", + "GetProductById", + "id", + "sdsd", "" + }; + + // Attribute Route, name:alpha constraint + yield return new object[] + { + "InlineConstraints_Products", + "GetProductByName", + "name", + "zxcv", + "/products/GetProductByName/zxcv" + }; + + // Attribute Route, name:length(1,20)? constraint + yield return new object[] + { + "InlineConstraints_Products", + "GetProductByCategoryName", + "name", + "sports", + "/products/GetProductByCategoryName/sports" + }; + + // Attribute Route, name:length(1,20)? constraint + yield return new object[] + { + "InlineConstraints_Products", + "GetProductByCategoryName", + null, + null, + "/products/GetProductByCategoryName" + }; + + // Attribute Route, catId:int:range(10, 100) constraint + yield return new object[] + { + "InlineConstraints_Products", + "GetProductByCategoryId", + "catId", + "50", + "/products/GetProductByCategoryId/50" + }; + + // Attribute Route, catId:int:range(10, 100) constraint + yield return new object[] + { + "InlineConstraints_Products", + "GetProductByCategoryId", + "catId", + "500", + "" + }; + + // Attribute Route, name:length(1,20)? constraint + yield return new object[] + { + "InlineConstraints_Products", + "GetProductByPrice", + "price", + "123.45", + "/products/GetProductByPrice/123.45" + }; + + // Attribute Route, price:float? constraint + yield return new object[] + { + "InlineConstraints_Products", + "GetProductByManufacturerId", + "manId", + "15", + "/products/GetProductByManufacturerId/15" + }; + + // Attribute Route, manId:int:min(10)? constraint + yield return new object[] + { + "InlineConstraints_Products", + "GetProductByManufacturerId", + "manId", + "qwer", + "" + }; + + // Attribute Route, manId:int:min(10)? constraint + yield return new object[] + { + "InlineConstraints_Products", + "GetProductByManufacturerId", + "manId", + "1", + "" + }; + + // Attribute Route, manId:int:min(10)? constraint + yield return new object[] + { + "InlineConstraints_Products", + "GetProductByManufacturerId", + "manId", + "1", + "" + }; + + // Attribute Route, dateTime:datetime constraint + yield return new object[] + { + "InlineConstraints_Products", + "GetProductByManufacturingDate", + "dateTime", + "2014-10-11T13:45:30", + "/products/GetProductByManufacturingDate/2014-10-11T13%3a45%3a30" + }; + + // Conventional Route, id:guid? constraint + yield return new object[] + { + "InlineConstraints_Store", + "GetStoreById", + "id", + "691cf17a-791b-4af8-99fd-e739e168170f", + "/store/GetStoreById/691cf17a-791b-4af8-99fd-e739e168170f" + }; + } + } + + [Theory] + [MemberData(nameof(QueryParameters))] + public async Task GetGeneratedLink( + string controller, + string action, + string parameterName, + string parameterValue, + string expectedLink) + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + string url; + + if (parameterName == null) + { + url = string.Format( + "{0}newController={1}&newAction={2}", + "http://localhost/products/GetGeneratedLink?", + controller, + action); + } + else + { + url = string.Format( + "{0}newController={1}&newAction={2}&{3}={4}", + "http://localhost/products/GetGeneratedLink?", + controller, + action, + parameterName, + parameterValue); + } + + var response = await client.GetAsync(url); + + // Assert + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal(expectedLink, body); + } + + private async Task> GetResponseValues(HttpResponseMessage response) + { + var body = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject>(body); + } } -} +} \ No newline at end of file diff --git a/test/WebSites/InlineConstraintsWebSite/Controllers/ProductsController.cs b/test/WebSites/InlineConstraintsWebSite/Controllers/ProductsController.cs new file mode 100644 index 0000000000..8c4d3b8adf --- /dev/null +++ b/test/WebSites/InlineConstraintsWebSite/Controllers/ProductsController.cs @@ -0,0 +1,77 @@ +// 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; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace InlineConstraintsWebSite.Controllers +{ + [Route("products/[action]")] + public class InlineConstraints_ProductsController : Controller + { + public IDictionary Index() + { + return ActionContext.RouteData.Values; + } + + [HttpGet("{id:int?}")] + public IDictionary GetProductById(int id) + { + return ActionContext.RouteData.Values; + } + + [HttpGet("{name:alpha}")] + public IDictionary GetProductByName(string name) + { + return ActionContext.RouteData.Values; + } + + [HttpGet("{dateTime:datetime}")] + public IDictionary GetProductByManufacturingDate(DateTime dateTime) + { + return ActionContext.RouteData.Values; + } + + [HttpGet("{name:length(1,20)?}")] + public IDictionary GetProductByCategoryName(string name) + { + return ActionContext.RouteData.Values; + } + + [HttpGet("{catId:int:range(10, 100)}")] + public IDictionary GetProductByCategoryId(int catId) + { + return ActionContext.RouteData.Values; + } + + [HttpGet("{price:float?}")] + public IDictionary GetProductByPrice(float price) + { + return ActionContext.RouteData.Values; + } + + [HttpGet("{manId:int:min(10)?}")] + public IDictionary GetProductByManufacturerId(int manId) + { + return ActionContext.RouteData.Values; + } + + [HttpGet(@"{name:regex(^abc$)}")] + public IDictionary GetUserByName(string name) + { + return ActionContext.RouteData.Values; + } + + public string GetGeneratedLink() + { + var query = ActionContext.HttpContext.Request.Query; + var values = query + .Where(kvp => kvp.Key != "newAction" && kvp.Key != "newController") + .ToDictionary(kvp => kvp.Key, kvp => (object)kvp.Value[0]); + + return Url.Action(query["newAction"], query["newController"], values); + } + } +} \ No newline at end of file diff --git a/test/WebSites/InlineConstraintsWebSite/Controllers/StoreController.cs b/test/WebSites/InlineConstraintsWebSite/Controllers/StoreController.cs new file mode 100644 index 0000000000..48aa995027 --- /dev/null +++ b/test/WebSites/InlineConstraintsWebSite/Controllers/StoreController.cs @@ -0,0 +1,22 @@ +// 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; +using System; +using System.Collections.Generic; + +namespace InlineConstraintsWebSite.Controllers +{ + public class InlineConstraints_StoreController : Controller + { + public IDictionary GetStoreById(Guid id) + { + return ActionContext.RouteData.Values; + } + + public IDictionary GetStoreByLocation(string location) + { + return ActionContext.RouteData.Values; + } + } +} \ No newline at end of file diff --git a/test/WebSites/InlineConstraintsWebSite/InlineConstraintsWebSite.kproj b/test/WebSites/InlineConstraintsWebSite/InlineConstraintsWebSite.kproj index 691753fec3..1ed652f5f9 100644 --- a/test/WebSites/InlineConstraintsWebSite/InlineConstraintsWebSite.kproj +++ b/test/WebSites/InlineConstraintsWebSite/InlineConstraintsWebSite.kproj @@ -1,8 +1,9 @@ - + 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + 45283 diff --git a/test/WebSites/InlineConstraintsWebSite/Startup.cs b/test/WebSites/InlineConstraintsWebSite/Startup.cs index 6b8a07e69a..9f455c0d28 100644 --- a/test/WebSites/InlineConstraintsWebSite/Startup.cs +++ b/test/WebSites/InlineConstraintsWebSite/Startup.cs @@ -4,6 +4,7 @@ using System; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Routing; +using Microsoft.AspNet.Routing.Constraints; using Microsoft.Framework.DependencyInjection; namespace InlineConstraints @@ -23,6 +24,15 @@ namespace InlineConstraints app.UseMvc(routes => { + routes.MapRoute("StoreId", + "store/{action}/{id:guid?}", + defaults: new { controller = "InlineConstraints_Store" }); + + routes.MapRoute("StoreLocation", + "store/{action}/{location:minlength(3):maxlength(10)}", + defaults: new { controller = "InlineConstraints_Store" }, + constraints: new { location = new AlphaRouteConstraint() }); + // Used by tests for the 'exists' constraint. routes.MapRoute("areaExists-area", "area-exists/{area:exists}/{controller=Home}/{action=Index}"); routes.MapRoute("areaExists", "area-exists/{controller=Home}/{action=Index}");