[Fixes #4991] Misleading MissingMethodException message for incorrect routes
This commit is contained in:
parent
d20cb17c52
commit
714c3d6659
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Builder
|
|||
/// <param name="name">The name of the route.</param>
|
||||
/// <param name="template">The URL pattern of the route.</param>
|
||||
/// <param name="defaults">
|
||||
/// 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.
|
||||
/// </param>
|
||||
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -442,6 +442,22 @@ namespace Microsoft.AspNetCore.Routing
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("UnableToFindServices"), p0, p1, p2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An error occurred while creating the route with name '{0}' and template '{1}'.
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_Exception
|
||||
{
|
||||
get { return GetString("TemplateRoute_Exception"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An error occurred while creating the route with name '{0}' and template '{1}'.
|
||||
/// </summary>
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -198,4 +198,7 @@
|
|||
<data name="UnableToFindServices" xml:space="preserve">
|
||||
<value>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.</value>
|
||||
</data>
|
||||
<data name="TemplateRoute_Exception" xml:space="preserve">
|
||||
<value>An error occurred while creating the route with name '{0}' and template '{1}'.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -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<string, IRouteConstraint> Constraints { get; protected set; }
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown for invalid routes or constraints.
|
||||
/// </summary>
|
||||
public class RouteCreationException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RouteCreationException"/> class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public RouteCreationException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RouteCreationException"/> class with a specified error message
|
||||
/// and a reference to the inner exception that is the cause of this exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception.</param>
|
||||
public RouteCreationException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,8 +46,9 @@ namespace Microsoft.AspNetCore.Routing.Tests
|
|||
public void ResolveConstraint_IntConstraintWithArgument_Throws()
|
||||
{
|
||||
// Arrange, Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(
|
||||
var ex = Assert.Throws<RouteCreationException>(
|
||||
() => _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<InvalidOperationException>(() => resolver.ResolveConstraint("custom"));
|
||||
var ex = Assert.Throws<RouteCreationException>(() => 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<InvalidOperationException>(() => resolver.ResolveConstraint("custom(5,6)"));
|
||||
var ex = Assert.Throws<RouteCreationException>(() => 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<InvalidOperationException>(() => _constraintResolver.ResolveConstraint("int(5,6)"));
|
||||
var ex = Assert.Throws<RouteCreationException>(() => _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);
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
var builder = CreateBuilder("{controller}/{action}");
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.Throws<InvalidOperationException>(
|
||||
ExceptionAssert.Throws<RouteCreationException>(
|
||||
() => 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<OptionalRouteConstraint>(Assert.Single(result).Value);
|
||||
var optionalConstraint = (OptionalRouteConstraint)result.First().Value;
|
||||
var optionalConstraint = (OptionalRouteConstraint)result.First().Value;
|
||||
var compositeConstraint = Assert.IsType<CompositeRouteConstraint>(optionalConstraint.InnerConstraint); ;
|
||||
Assert.Equal(compositeConstraint.Constraints.Count(), 2);
|
||||
|
||||
|
|
|
|||
|
|
@ -30,11 +30,8 @@ namespace Microsoft.AspNetCore.Routing
|
|||
// Arrange
|
||||
var template = @"{controller}/{action}/ {p1:regex(abc} ";
|
||||
var mockTarget = new Mock<IRouter>(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<InvalidOperationException>(
|
||||
var exception = Assert.Throws<RouteCreationException>(
|
||||
() => 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<InvalidOperationException>(
|
||||
var expectedMessage = "An error occurred while creating the route with name 'mockName' and template" +
|
||||
" '{controller}/{action}'.";
|
||||
|
||||
var exception = ExceptionAssert.Throws<RouteCreationException>(
|
||||
() => 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]
|
||||
|
|
|
|||
|
|
@ -58,46 +58,54 @@ namespace Microsoft.AspNetCore.Routing.Tests
|
|||
var routeBuilder = CreateRouteBuilder();
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(
|
||||
var ex = Assert.Throws<RouteCreationException>(
|
||||
() => 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<ArgumentException>(
|
||||
var ex = Assert.Throws<RouteCreationException>(
|
||||
() => 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<ArgumentException>(() =>
|
||||
var ex = Assert.Throws<RouteCreationException>(() =>
|
||||
{
|
||||
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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue