Implement RouteKeyHandling.CatchAll
This commit is contained in:
parent
8bfb6eb8d5
commit
b72b44c20c
|
|
@ -1226,6 +1226,22 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
return GetString("AttributeRoute_TokenReplacement_UnescapedBraceInToken");
|
return GetString("AttributeRoute_TokenReplacement_UnescapedBraceInToken");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value must be either '{0}' or '{1}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string RouteConstraintAttribute_InvalidKeyHandlingValue
|
||||||
|
{
|
||||||
|
get { return GetString("RouteConstraintAttribute_InvalidKeyHandlingValue"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value must be either '{0}' or '{1}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatRouteConstraintAttribute_InvalidKeyHandlingValue(object p0, object p1)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("RouteConstraintAttribute_InvalidKeyHandlingValue"), p0, p1);
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -215,9 +215,18 @@ namespace Microsoft.AspNet.Mvc
|
||||||
// Skip duplicates
|
// Skip duplicates
|
||||||
if (!HasConstraint(actionDescriptor.RouteConstraints, constraintAttribute.RouteKey))
|
if (!HasConstraint(actionDescriptor.RouteConstraints, constraintAttribute.RouteKey))
|
||||||
{
|
{
|
||||||
actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
|
if (constraintAttribute.RouteValue == null)
|
||||||
constraintAttribute.RouteKey,
|
{
|
||||||
constraintAttribute.RouteValue));
|
actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
|
||||||
|
constraintAttribute.RouteKey,
|
||||||
|
constraintAttribute.RouteKeyHandling));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
|
||||||
|
constraintAttribute.RouteKey,
|
||||||
|
constraintAttribute.RouteValue));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -229,7 +238,13 @@ namespace Microsoft.AspNet.Mvc
|
||||||
// want to provide these values as ambient values.
|
// want to provide these values as ambient values.
|
||||||
foreach (var constraint in actionDescriptor.RouteConstraints)
|
foreach (var constraint in actionDescriptor.RouteConstraints)
|
||||||
{
|
{
|
||||||
actionDescriptor.RouteValueDefaults.Add(constraint.RouteKey, constraint.RouteValue);
|
// We don't need to do anything with attribute routing for 'catch all' behavior. Order
|
||||||
|
// and predecedence of attribute routes allow this kind of behavior.
|
||||||
|
if (constraint.KeyHandling == RouteKeyHandling.RequireKey ||
|
||||||
|
constraint.KeyHandling == RouteKeyHandling.DenyKey)
|
||||||
|
{
|
||||||
|
actionDescriptor.RouteValueDefaults.Add(constraint.RouteKey, constraint.RouteValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replaces tokens like [controller]/[action] in the route template with the actual values
|
// Replaces tokens like [controller]/[action] in the route template with the actual values
|
||||||
|
|
|
||||||
|
|
@ -348,4 +348,7 @@
|
||||||
<data name="AttributeRoute_TokenReplacement_UnescapedBraceInToken" xml:space="preserve">
|
<data name="AttributeRoute_TokenReplacement_UnescapedBraceInToken" xml:space="preserve">
|
||||||
<value>An unescaped '[' token is not allowed inside of a replacement token. Use '[[' to escape.</value>
|
<value>An unescaped '[' token is not allowed inside of a replacement token. Use '[[' to escape.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="RouteConstraintAttribute_InvalidKeyHandlingValue" xml:space="preserve">
|
||||||
|
<value>The value must be either '{0}' or '{1}'.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -2,12 +2,59 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An attribute which specifies a required route value for an action or controller.
|
||||||
|
///
|
||||||
|
/// When placed on an action, the route data of a request must match the expectations of the route
|
||||||
|
/// constraint in order for the action to be selected. See <see cref="RouteKeyHandling"/> for
|
||||||
|
/// the expectations that must be satisfied by the route data.
|
||||||
|
///
|
||||||
|
/// When placed on a controller, unless overridden by the action, the constraint applies to all
|
||||||
|
/// actions defined by the controller.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||||
public abstract class RouteConstraintAttribute : Attribute
|
public abstract class RouteConstraintAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="RouteConstraintAttribute"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="routeKey">The route value key.</param>
|
||||||
|
/// <param name="keyHandling">
|
||||||
|
/// The <see cref="RouteKeyHandling"/> value. Must be <see cref="RouteKeyHandling.CatchAll "/>
|
||||||
|
/// or <see cref="RouteKeyHandling.DenyKey"/>.
|
||||||
|
/// </param>
|
||||||
|
protected RouteConstraintAttribute(
|
||||||
|
[NotNull] string routeKey,
|
||||||
|
RouteKeyHandling keyHandling)
|
||||||
|
{
|
||||||
|
RouteKey = routeKey;
|
||||||
|
RouteKeyHandling = keyHandling;
|
||||||
|
|
||||||
|
if (keyHandling != RouteKeyHandling.CatchAll &&
|
||||||
|
keyHandling != RouteKeyHandling.DenyKey)
|
||||||
|
{
|
||||||
|
var message = Resources.FormatRouteConstraintAttribute_InvalidKeyHandlingValue(
|
||||||
|
Enum.GetName(typeof(RouteKeyHandling), RouteKeyHandling.CatchAll),
|
||||||
|
Enum.GetName(typeof(RouteKeyHandling), RouteKeyHandling.DenyKey));
|
||||||
|
throw new ArgumentException(message, "keyHandling");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="RouteConstraintAttribute"/> with
|
||||||
|
/// <see cref="RouteConstraintAttribute.RouteKeyHandling"/> set to <see cref="RouteKeyHandling.RequireKey"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="routeKey">The route value key.</param>
|
||||||
|
/// <param name="routeValue">The expected route value.</param>
|
||||||
|
/// <param name="blockNonAttributedActions">
|
||||||
|
/// Set to true to negate this constraint on all actions that do not define a behavior for this route key.
|
||||||
|
/// </param>
|
||||||
protected RouteConstraintAttribute(
|
protected RouteConstraintAttribute(
|
||||||
[NotNull]string routeKey,
|
[NotNull]string routeKey,
|
||||||
[NotNull]string routeValue,
|
[NotNull]string routeValue,
|
||||||
|
|
@ -18,8 +65,25 @@ namespace Microsoft.AspNet.Mvc
|
||||||
BlockNonAttributedActions = blockNonAttributedActions;
|
BlockNonAttributedActions = blockNonAttributedActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The route value key.
|
||||||
|
/// </summary>
|
||||||
public string RouteKey { get; private set; }
|
public string RouteKey { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="RouteKeyHandling"/>.
|
||||||
|
/// </summary>
|
||||||
|
public RouteKeyHandling RouteKeyHandling { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The expected route value. Will be null unless <see cref="RouteConstraintAttribute.RouteKeyHandling"/> is
|
||||||
|
/// set to <see cref="RouteKeyHandling.RequireKey"/>.
|
||||||
|
/// </summary>
|
||||||
public string RouteValue { get; private set; }
|
public string RouteValue { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set to true to negate this constraint on all actions that do not define a behavior for this route key.
|
||||||
|
/// </summary>
|
||||||
public bool BlockNonAttributedActions { get; private set; }
|
public bool BlockNonAttributedActions { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,92 @@ namespace Microsoft.AspNet.Mvc
|
||||||
Assert.Same(action, actionWithConstraints);
|
Assert.Same(action, actionWithConstraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SelectAsync_WithCatchAll_PrefersNonCatchAll()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var actions = new ActionDescriptor[]
|
||||||
|
{
|
||||||
|
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||||
|
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||||
|
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||||
|
};
|
||||||
|
|
||||||
|
actions[0].RouteConstraints.Add(new RouteDataActionConstraint("country", "CA"));
|
||||||
|
actions[1].RouteConstraints.Add(new RouteDataActionConstraint("country", "US"));
|
||||||
|
actions[2].RouteConstraints.Add(new RouteDataActionConstraint("country", RouteKeyHandling.CatchAll));
|
||||||
|
|
||||||
|
var selector = CreateSelector(actions);
|
||||||
|
var context = CreateRouteContext("GET");
|
||||||
|
|
||||||
|
context.RouteData.Values.Add("controller", "Store");
|
||||||
|
context.RouteData.Values.Add("action", "Buy");
|
||||||
|
context.RouteData.Values.Add("country", "CA");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var action = await selector.SelectAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(action, actions[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SelectAsync_WithCatchAll_CatchAllIsOnlyMatch()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var actions = new ActionDescriptor[]
|
||||||
|
{
|
||||||
|
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||||
|
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||||
|
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||||
|
};
|
||||||
|
|
||||||
|
actions[0].RouteConstraints.Add(new RouteDataActionConstraint("country", "CA"));
|
||||||
|
actions[1].RouteConstraints.Add(new RouteDataActionConstraint("country", "US"));
|
||||||
|
actions[2].RouteConstraints.Add(new RouteDataActionConstraint("country", RouteKeyHandling.CatchAll));
|
||||||
|
|
||||||
|
var selector = CreateSelector(actions);
|
||||||
|
var context = CreateRouteContext("GET");
|
||||||
|
|
||||||
|
context.RouteData.Values.Add("controller", "Store");
|
||||||
|
context.RouteData.Values.Add("action", "Buy");
|
||||||
|
context.RouteData.Values.Add("country", "DE");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var action = await selector.SelectAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(action, actions[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SelectAsync_WithCatchAll_NoMatch()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var actions = new ActionDescriptor[]
|
||||||
|
{
|
||||||
|
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||||
|
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||||
|
CreateAction(area: null, controller: "Store", action: "Buy"),
|
||||||
|
};
|
||||||
|
|
||||||
|
actions[0].RouteConstraints.Add(new RouteDataActionConstraint("country", "CA"));
|
||||||
|
actions[1].RouteConstraints.Add(new RouteDataActionConstraint("country", "US"));
|
||||||
|
actions[2].RouteConstraints.Add(new RouteDataActionConstraint("country", RouteKeyHandling.CatchAll));
|
||||||
|
|
||||||
|
var selector = CreateSelector(actions);
|
||||||
|
var context = CreateRouteContext("GET");
|
||||||
|
|
||||||
|
context.RouteData.Values.Add("controller", "Store");
|
||||||
|
context.RouteData.Values.Add("action", "Buy");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var action = await selector.SelectAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(action);
|
||||||
|
}
|
||||||
|
|
||||||
private static ActionDescriptor[] GetActions()
|
private static ActionDescriptor[] GetActions()
|
||||||
{
|
{
|
||||||
return new ActionDescriptor[]
|
return new ActionDescriptor[]
|
||||||
|
|
@ -268,6 +354,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
Name = string.Format("Area: {0}, Controller: {1}, Action: {2}", area, controller, action),
|
Name = string.Format("Area: {0}, Controller: {1}, Action: {2}", area, controller, action),
|
||||||
RouteConstraints = new List<RouteDataActionConstraint>(),
|
RouteConstraints = new List<RouteDataActionConstraint>(),
|
||||||
|
Parameters = new List<ParameterDescriptor>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
actionDescriptor.RouteConstraints.Add(
|
actionDescriptor.RouteConstraints.Add(
|
||||||
|
|
|
||||||
|
|
@ -120,4 +120,4 @@
|
||||||
<Compile Include="ViewResultTest.cs" />
|
<Compile Include="ViewResultTest.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -817,6 +817,110 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
Assert.Equal("/Admin/Users/All", result.Link);
|
Assert.Equal("/Admin/Users/All", result.Link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ControllerWithCatchAll_CanReachSpecificCountry()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.Handler;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/api/Products/US/GetProducts");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(200, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.ReadBodyAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
Assert.Contains("/api/Products/US/GetProducts", result.ExpectedUrls);
|
||||||
|
Assert.Equal("Products", result.Controller);
|
||||||
|
Assert.Equal("GetProducts", result.Action);
|
||||||
|
Assert.Equal(
|
||||||
|
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
{ "country", "US" },
|
||||||
|
{ "action", "GetProducts" },
|
||||||
|
{ "controller", "Products" },
|
||||||
|
},
|
||||||
|
result.RouteValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The 'default' route doesn't provide a value for {country}
|
||||||
|
[Fact]
|
||||||
|
public async Task ControllerWithCatchAll_CannotReachWithoutCountry()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.Handler;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/Products/GetProducts");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(404, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ControllerWithCatchAll_GenerateLinkForSpecificCountry()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.Handler;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var url =
|
||||||
|
LinkFrom("http://localhost/")
|
||||||
|
.To(new { action = "GetProducts", controller = "Products", country = "US" });
|
||||||
|
var response = await client.GetAsync(url);
|
||||||
|
|
||||||
|
var body = await response.ReadBodyAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("/api/Products/US/GetProducts", result.Link);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ControllerWithCatchAll_GenerateLinkForFallback()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.Handler;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var url =
|
||||||
|
LinkFrom("http://localhost/")
|
||||||
|
.To(new { action = "GetProducts", controller = "Products", country = "CA" });
|
||||||
|
var response = await client.GetAsync(url);
|
||||||
|
|
||||||
|
var body = await response.ReadBodyAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("/api/Products/CA/GetProducts", result.Link);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ControllerWithCatchAll_GenerateLink_FailsWithoutCountry()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.Handler;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var url =
|
||||||
|
LinkFrom("http://localhost/")
|
||||||
|
.To(new { action = "GetProducts", controller = "Products", country = (string)null });
|
||||||
|
var response = await client.GetAsync(url);
|
||||||
|
|
||||||
|
var body = await response.ReadBodyAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(result.Link);
|
||||||
|
}
|
||||||
|
|
||||||
private static LinkBuilder LinkFrom(string url)
|
private static LinkBuilder LinkFrom(string url)
|
||||||
{
|
{
|
||||||
return new LinkBuilder(url);
|
return new LinkBuilder(url);
|
||||||
|
|
|
||||||
|
|
@ -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 RoutingWebSite.Products
|
||||||
|
{
|
||||||
|
[CountryNeutral]
|
||||||
|
public class ProductsController : Controller
|
||||||
|
{
|
||||||
|
private readonly TestResponseGenerator _generator;
|
||||||
|
|
||||||
|
public ProductsController(TestResponseGenerator generator)
|
||||||
|
{
|
||||||
|
_generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult GetProducts()
|
||||||
|
{
|
||||||
|
return _generator.Generate("/api/Products/CA/GetProducts");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 RoutingWebSite.Products.US
|
||||||
|
{
|
||||||
|
[CountrySpecific("US")]
|
||||||
|
public class ProductsController : Controller
|
||||||
|
{
|
||||||
|
private readonly TestResponseGenerator _generator;
|
||||||
|
|
||||||
|
public ProductsController(TestResponseGenerator generator)
|
||||||
|
{
|
||||||
|
_generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult GetProducts()
|
||||||
|
{
|
||||||
|
return _generator.Generate("/api/Products/US/GetProducts");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// 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 RoutingWebSite
|
||||||
|
{
|
||||||
|
public class CountryNeutralAttribute : RouteConstraintAttribute
|
||||||
|
{
|
||||||
|
public CountryNeutralAttribute()
|
||||||
|
: base("country", RouteKeyHandling.CatchAll)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// 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 RoutingWebSite
|
||||||
|
{
|
||||||
|
public class CountrySpecificAttribute : RouteConstraintAttribute
|
||||||
|
{
|
||||||
|
public CountrySpecificAttribute(string countryCode)
|
||||||
|
: base("country", countryCode, blockNonAttributedActions: false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -35,9 +35,13 @@
|
||||||
<Compile Include="Controllers\BlogController.cs" />
|
<Compile Include="Controllers\BlogController.cs" />
|
||||||
<Compile Include="Controllers\EmployeeController.cs" />
|
<Compile Include="Controllers\EmployeeController.cs" />
|
||||||
<Compile Include="Controllers\HomeController.cs" />
|
<Compile Include="Controllers\HomeController.cs" />
|
||||||
|
<Compile Include="Controllers\Products\ProductsController.cs" />
|
||||||
|
<Compile Include="Controllers\Products\US\ProductsController.cs" />
|
||||||
<Compile Include="Controllers\StoreController.cs" />
|
<Compile Include="Controllers\StoreController.cs" />
|
||||||
<Compile Include="Controllers\TeamController.cs" />
|
<Compile Include="Controllers\TeamController.cs" />
|
||||||
<Compile Include="HttpMergeAttribute.cs" />
|
<Compile Include="HttpMergeAttribute.cs" />
|
||||||
|
<Compile Include="CountryNeutralAttribute.cs" />
|
||||||
|
<Compile Include="CountrySpecificAttribute.cs" />
|
||||||
<Compile Include="Startup.cs" />
|
<Compile Include="Startup.cs" />
|
||||||
<Compile Include="TestResponseGenerator.cs" />
|
<Compile Include="TestResponseGenerator.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
using Microsoft.AspNet.Builder;
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
using Microsoft.AspNet.Mvc;
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
using Microsoft.Framework.ConfigurationModel;
|
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
namespace RoutingWebSite
|
namespace RoutingWebSite
|
||||||
|
|
@ -27,6 +28,11 @@ namespace RoutingWebSite
|
||||||
|
|
||||||
routes.MapRoute("ActionAsMethod", "{controller}/{action}",
|
routes.MapRoute("ActionAsMethod", "{controller}/{action}",
|
||||||
defaults: new { controller = "Home", action = "Index" });
|
defaults: new { controller = "Home", action = "Index" });
|
||||||
|
|
||||||
|
routes.MapRoute(
|
||||||
|
"products",
|
||||||
|
"api/Products/{country}/{action}",
|
||||||
|
defaults: new { controller = "Products" });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue