diff --git a/src/Microsoft.AspNetCore.Routing/DefaultInlineConstraintResolver.cs b/src/Microsoft.AspNetCore.Routing/DefaultInlineConstraintResolver.cs
index 5d335f0dd9..7426516db2 100644
--- a/src/Microsoft.AspNetCore.Routing/DefaultInlineConstraintResolver.cs
+++ b/src/Microsoft.AspNetCore.Routing/DefaultInlineConstraintResolver.cs
@@ -69,12 +69,25 @@ namespace Microsoft.AspNetCore.Routing
if (!typeof(IRouteConstraint).GetTypeInfo().IsAssignableFrom(constraintType.GetTypeInfo()))
{
- throw new InvalidOperationException(
+ throw new RouteCreationException(
Resources.FormatDefaultInlineConstraintResolver_TypeNotConstraint(
constraintType, constraintKey, typeof(IRouteConstraint).Name));
}
- return CreateConstraint(constraintType, argumentString);
+ try
+ {
+ return CreateConstraint(constraintType, argumentString);
+ }
+ catch (RouteCreationException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new RouteCreationException(
+ $"An error occurred while trying to create an instance of route constraint '{constraintType.FullName}'.",
+ exception);
+ }
}
private static IRouteConstraint CreateConstraint(Type constraintType, string argumentString)
@@ -107,7 +120,7 @@ namespace Microsoft.AspNetCore.Routing
if (constructorMatches == 0)
{
- throw new InvalidOperationException(
+ throw new RouteCreationException(
Resources.FormatDefaultInlineConstraintResolver_CouldNotFindCtor(
constraintTypeInfo.Name, arguments.Length));
}
@@ -118,7 +131,7 @@ namespace Microsoft.AspNetCore.Routing
}
else
{
- throw new InvalidOperationException(
+ throw new RouteCreationException(
Resources.FormatDefaultInlineConstraintResolver_AmbiguousCtors(
constraintTypeInfo.Name, arguments.Length));
}
diff --git a/src/Microsoft.AspNetCore.Routing/MapRouteRouteBuilderExtensions.cs b/src/Microsoft.AspNetCore.Routing/MapRouteRouteBuilderExtensions.cs
index cf46749f3d..d3cc115e6d 100644
--- a/src/Microsoft.AspNetCore.Routing/MapRouteRouteBuilderExtensions.cs
+++ b/src/Microsoft.AspNetCore.Routing/MapRouteRouteBuilderExtensions.cs
@@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Builder
/// The name of the route.
/// The URL pattern of the route.
///
- /// An object that contains default values for route parameters. The object's properties represent the names
+ /// An object that contains default values for route parameters. The object's properties represent the names
/// and values of the default values.
///
/// A reference to this instance after the operation has completed.
@@ -104,7 +104,7 @@ namespace Microsoft.AspNetCore.Builder
{
if (routeBuilder.DefaultHandler == null)
{
- throw new InvalidOperationException(Resources.DefaultHandler_MustBeSet);
+ throw new RouteCreationException(Resources.DefaultHandler_MustBeSet);
}
var inlineConstraintResolver = routeBuilder
diff --git a/src/Microsoft.AspNetCore.Routing/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Routing/Properties/Resources.Designer.cs
index 6abdc4ea48..9dd91e7f85 100644
--- a/src/Microsoft.AspNetCore.Routing/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNetCore.Routing/Properties/Resources.Designer.cs
@@ -442,6 +442,22 @@ namespace Microsoft.AspNetCore.Routing
return string.Format(CultureInfo.CurrentCulture, GetString("UnableToFindServices"), p0, p1, p2);
}
+ ///
+ /// An error occurred while creating the route with name '{0}' and template '{1}'.
+ ///
+ internal static string TemplateRoute_Exception
+ {
+ get { return GetString("TemplateRoute_Exception"); }
+ }
+
+ ///
+ /// An error occurred while creating the route with name '{0}' and template '{1}'.
+ ///
+ internal static string FormatTemplateRoute_Exception(object p0, object p1)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("TemplateRoute_Exception"), p0, p1);
+ }
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/src/Microsoft.AspNetCore.Routing/Resources.resx b/src/Microsoft.AspNetCore.Routing/Resources.resx
index 97217dfa25..6af7dff663 100644
--- a/src/Microsoft.AspNetCore.Routing/Resources.resx
+++ b/src/Microsoft.AspNetCore.Routing/Resources.resx
@@ -198,4 +198,7 @@
Unable to find the required services. Please add all the required services by calling '{0}.{1}' inside the call to '{2}' in the application startup code.
+
+ An error occurred while creating the route with name '{0}' and template '{1}'.
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Routing/RouteBase.cs b/src/Microsoft.AspNetCore.Routing/RouteBase.cs
index 05b70c6cfb..f05d2d5470 100644
--- a/src/Microsoft.AspNetCore.Routing/RouteBase.cs
+++ b/src/Microsoft.AspNetCore.Routing/RouteBase.cs
@@ -46,12 +46,19 @@ namespace Microsoft.AspNetCore.Routing
ConstraintResolver = constraintResolver;
DataTokens = dataTokens ?? new RouteValueDictionary();
- // Data we parse from the template will be used to fill in the rest of the constraints or
- // defaults. The parser will throw for invalid routes.
- ParsedTemplate = TemplateParser.Parse(template);
+ try
+ {
+ // Data we parse from the template will be used to fill in the rest of the constraints or
+ // defaults. The parser will throw for invalid routes.
+ ParsedTemplate = TemplateParser.Parse(template);
- Constraints = GetConstraints(constraintResolver, ParsedTemplate, constraints);
- Defaults = GetDefaults(ParsedTemplate, defaults);
+ Constraints = GetConstraints(constraintResolver, ParsedTemplate, constraints);
+ Defaults = GetDefaults(ParsedTemplate, defaults);
+ }
+ catch (Exception exception)
+ {
+ throw new RouteCreationException(Resources.FormatTemplateRoute_Exception(name, template), exception);
+ }
}
public virtual IDictionary Constraints { get; protected set; }
diff --git a/src/Microsoft.AspNetCore.Routing/RouteConstraintBuilder.cs b/src/Microsoft.AspNetCore.Routing/RouteConstraintBuilder.cs
index bd2dfca275..c230ed0e96 100644
--- a/src/Microsoft.AspNetCore.Routing/RouteConstraintBuilder.cs
+++ b/src/Microsoft.AspNetCore.Routing/RouteConstraintBuilder.cs
@@ -111,7 +111,7 @@ namespace Microsoft.AspNetCore.Routing
var regexPattern = value as string;
if (regexPattern == null)
{
- throw new InvalidOperationException(
+ throw new RouteCreationException(
Resources.FormatRouteConstraintBuilder_ValidationMustBeStringOrCustomConstraint(
key,
value,
diff --git a/src/Microsoft.AspNetCore.Routing/RouteCreationException.cs b/src/Microsoft.AspNetCore.Routing/RouteCreationException.cs
new file mode 100644
index 0000000000..0c47e7e412
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Routing/RouteCreationException.cs
@@ -0,0 +1,33 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace Microsoft.AspNetCore.Routing
+{
+ ///
+ /// The exception that is thrown for invalid routes or constraints.
+ ///
+ public class RouteCreationException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class with a specified error message.
+ ///
+ /// The message that describes the error.
+ public RouteCreationException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with a specified error message
+ /// and a reference to the inner exception that is the cause of this exception.
+ ///
+ /// The error message that explains the reason for the exception.
+ /// The exception that is the cause of the current exception.
+ public RouteCreationException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Routing.Tests/DefaultInlineConstraintResolverTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/DefaultInlineConstraintResolverTest.cs
index 128d50ee35..bba78fb6b2 100644
--- a/test/Microsoft.AspNetCore.Routing.Tests/DefaultInlineConstraintResolverTest.cs
+++ b/test/Microsoft.AspNetCore.Routing.Tests/DefaultInlineConstraintResolverTest.cs
@@ -46,8 +46,9 @@ namespace Microsoft.AspNetCore.Routing.Tests
public void ResolveConstraint_IntConstraintWithArgument_Throws()
{
// Arrange, Act & Assert
- var ex = Assert.Throws(
+ var ex = Assert.Throws(
() => _constraintResolver.ResolveConstraint("int(5)"));
+
Assert.Equal("Could not find a constructor for constraint type 'IntRouteConstraint'" +
" with the following number of parameters: 1.",
ex.Message);
@@ -276,7 +277,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
var resolver = GetInlineConstraintResolver(routeOptions);
// Act & Assert
- var ex = Assert.Throws(() => resolver.ResolveConstraint("custom"));
+ var ex = Assert.Throws(() => resolver.ResolveConstraint("custom"));
Assert.Equal("The constraint type 'System.String' which is mapped to constraint key 'custom'" +
" must implement the 'IRouteConstraint' interface.",
ex.Message);
@@ -291,7 +292,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
var resolver = GetInlineConstraintResolver(routeOptions);
// Act & Assert
- var ex = Assert.Throws(() => resolver.ResolveConstraint("custom(5,6)"));
+ var ex = Assert.Throws(() => resolver.ResolveConstraint("custom(5,6)"));
Assert.Equal("The constructor to use for activating the constraint type 'MultiConstructorRouteConstraint' is ambiguous." +
" Multiple constructors were found with the following number of parameters: 2.",
ex.Message);
@@ -317,7 +318,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
{
// Arrange
// Act & Assert
- var ex = Assert.Throws(() => _constraintResolver.ResolveConstraint("int(5,6)"));
+ var ex = Assert.Throws(() => _constraintResolver.ResolveConstraint("int(5,6)"));
Assert.Equal("Could not find a constructor for constraint type 'IntRouteConstraint'" +
" with the following number of parameters: 2.",
ex.Message);
diff --git a/test/Microsoft.AspNetCore.Routing.Tests/RouteConstraintBuilderTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/RouteConstraintBuilderTest.cs
index 8aceb54da0..fefd735a81 100644
--- a/test/Microsoft.AspNetCore.Routing.Tests/RouteConstraintBuilderTest.cs
+++ b/test/Microsoft.AspNetCore.Routing.Tests/RouteConstraintBuilderTest.cs
@@ -78,7 +78,7 @@ namespace Microsoft.AspNetCore.Routing
var builder = CreateBuilder("{controller}/{action}");
// Act & Assert
- ExceptionAssert.Throws(
+ ExceptionAssert.Throws(
() => builder.AddConstraint("controller", 5),
"The constraint entry 'controller' - '5' on the route " +
"'{controller}/{action}' must have a string value or be of a type which implements '" +
@@ -117,7 +117,7 @@ namespace Microsoft.AspNetCore.Routing
[Fact]
public void AddResolvedConstraint_SetOptionalParameter_AfterAddingTheParameter()
{
- var builder = CreateBuilder("{controller}/{action}/{id}");
+ var builder = CreateBuilder("{controller}/{action}/{id}");
builder.AddResolvedConstraint("id", "int");
builder.SetOptional("id");
@@ -140,7 +140,7 @@ namespace Microsoft.AspNetCore.Routing
Assert.Equal(1, result.Count);
Assert.Equal("name", result.First().Key);
Assert.IsType(Assert.Single(result).Value);
- var optionalConstraint = (OptionalRouteConstraint)result.First().Value;
+ var optionalConstraint = (OptionalRouteConstraint)result.First().Value;
var compositeConstraint = Assert.IsType(optionalConstraint.InnerConstraint); ;
Assert.Equal(compositeConstraint.Constraints.Count(), 2);
diff --git a/test/Microsoft.AspNetCore.Routing.Tests/RouteTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/RouteTest.cs
index ac6333a0f6..ceab3f1d3d 100644
--- a/test/Microsoft.AspNetCore.Routing.Tests/RouteTest.cs
+++ b/test/Microsoft.AspNetCore.Routing.Tests/RouteTest.cs
@@ -30,11 +30,8 @@ namespace Microsoft.AspNetCore.Routing
// Arrange
var template = @"{controller}/{action}/ {p1:regex(abc} ";
var mockTarget = new Mock(MockBehavior.Strict);
- var expected = "The constraint entry 'p1' - 'regex(abc' on the route " +
- "'{controller}/{action}/ {p1:regex(abc} ' could not be resolved by the constraint resolver of type " +
- "'IInlineConstraintResolverProxy'.";
- var exception = Assert.Throws(
+ var exception = Assert.Throws(
() => new Route(
mockTarget.Object,
template,
@@ -43,7 +40,15 @@ namespace Microsoft.AspNetCore.Routing
dataTokens: null,
inlineConstraintResolver: _inlineConstraintResolver));
+ var expected = "An error occurred while creating the route with name '' and template" +
+ $" '{template}'.";
Assert.Equal(expected, exception.Message);
+
+ Assert.NotNull(exception.InnerException);
+ expected = "The constraint entry 'p1' - 'regex(abc' on the route " +
+ "'{controller}/{action}/ {p1:regex(abc} ' could not be resolved by the constraint resolver of type " +
+ "'IInlineConstraintResolverProxy'.";
+ Assert.Equal(expected, exception.InnerException.Message);
}
[Fact]
@@ -1460,14 +1465,21 @@ namespace Microsoft.AspNetCore.Routing
var routeBuilder = CreateRouteBuilder();
// Assert
- ExceptionAssert.Throws(
+ var expectedMessage = "An error occurred while creating the route with name 'mockName' and template" +
+ " '{controller}/{action}'.";
+
+ var exception = ExceptionAssert.Throws(
() => routeBuilder.MapRoute("mockName",
"{controller}/{action}",
defaults: null,
constraints: new { controller = "a.*", action = 17 }),
- "The constraint entry 'action' - '17' on the route '{controller}/{action}' " +
+ expectedMessage);
+
+ expectedMessage = "The constraint entry 'action' - '17' on the route '{controller}/{action}' " +
"must have a string value or be of a type which implements '" +
- typeof(IRouteConstraint) + "'.");
+ typeof(IRouteConstraint) + "'.";
+ Assert.NotNull(exception.InnerException);
+ Assert.Equal(expectedMessage, exception.InnerException.Message);
}
[Fact]
diff --git a/test/Microsoft.AspNetCore.Routing.Tests/TemplateParserDefaultValuesTests.cs b/test/Microsoft.AspNetCore.Routing.Tests/TemplateParserDefaultValuesTests.cs
index 10c5651e84..96c126fd30 100644
--- a/test/Microsoft.AspNetCore.Routing.Tests/TemplateParserDefaultValuesTests.cs
+++ b/test/Microsoft.AspNetCore.Routing.Tests/TemplateParserDefaultValuesTests.cs
@@ -58,46 +58,54 @@ namespace Microsoft.AspNetCore.Routing.Tests
var routeBuilder = CreateRouteBuilder();
// Act & Assert
- var ex = Assert.Throws(
+ var ex = Assert.Throws(
() => routeBuilder.MapRoute("mockName",
"{controller}/{action}/{id:int=12}",
defaults: new { id = 13 },
constraints: null));
- var message = "The route parameter 'id' has both an inline default value and an explicit default" +
- " value specified. A route parameter cannot contain an inline default value when" +
- " a default value is specified explicitly. Consider removing one of them.";
+ var message = "An error occurred while creating the route with name 'mockName' and template" +
+ " '{controller}/{action}/{id:int=12}'.";
Assert.Equal(message, ex.Message);
+
+ Assert.NotNull(ex.InnerException);
+ message = "The route parameter 'id' has both an inline default value and an explicit default" +
+ " value specified. A route parameter cannot contain an inline default value when" +
+ " a default value is specified explicitly. Consider removing one of them.";
+ Assert.Equal(message, ex.InnerException.Message);
}
[Fact]
public void EmptyDefaultValue_WithOptionalParameter_Throws()
{
// Arrange
- var message = "An optional parameter cannot have default value." + Environment.NewLine +
- "Parameter name: routeTemplate";
var routeBuilder = CreateRouteBuilder();
// Act & Assert
- var ex = Assert.Throws(
+ var ex = Assert.Throws(
() => routeBuilder.MapRoute("mockName",
"{controller}/{action}/{id:int=?}",
defaults: new { id = 13 },
constraints: null));
+ var message = "An error occurred while creating the route with name 'mockName' and template" +
+ " '{controller}/{action}/{id:int=?}'.";
Assert.Equal(message, ex.Message);
+
+ Assert.NotNull(ex.InnerException);
+ message = "An optional parameter cannot have default value." + Environment.NewLine +
+ "Parameter name: routeTemplate";
+ Assert.Equal(message, ex.InnerException.Message);
}
[Fact]
public void NonEmptyDefaultValue_WithOptionalParameter_Throws()
{
// Arrange
- var message = "An optional parameter cannot have default value." + Environment.NewLine +
- "Parameter name: routeTemplate";
var routeBuilder = CreateRouteBuilder();
// Act & Assert
- var ex = Assert.Throws(() =>
+ var ex = Assert.Throws(() =>
{
routeBuilder.MapRoute(
"mockName",
@@ -106,7 +114,14 @@ namespace Microsoft.AspNetCore.Routing.Tests
constraints: null);
});
+ var message = "An error occurred while creating the route with name 'mockName' and template" +
+ " '{controller}/{action}/{id:int=12?}'.";
Assert.Equal(message, ex.Message);
+
+ Assert.NotNull(ex.InnerException);
+ message = "An optional parameter cannot have default value." + Environment.NewLine +
+ "Parameter name: routeTemplate";
+ Assert.Equal(message, ex.InnerException.Message);
}
private static IRouteBuilder CreateRouteBuilder()