Tolerate leading "~/" or "/" (#509)
This commit is contained in:
parent
93d20ec78c
commit
57697baedb
|
|
@ -249,7 +249,7 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
=> string.Format(CultureInfo.CurrentCulture, GetString("TemplateRoute_InvalidParameterName"), p0);
|
=> string.Format(CultureInfo.CurrentCulture, GetString("TemplateRoute_InvalidParameterName"), p0);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The route template cannot start with a '/' or '~' character.
|
/// The route template cannot start with a '~' character unless followed by a '/'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string TemplateRoute_InvalidRouteTemplate
|
internal static string TemplateRoute_InvalidRouteTemplate
|
||||||
{
|
{
|
||||||
|
|
@ -257,7 +257,7 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The route template cannot start with a '/' or '~' character.
|
/// The route template cannot start with a '~' character unless followed by a '/'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatTemplateRoute_InvalidRouteTemplate()
|
internal static string FormatTemplateRoute_InvalidRouteTemplate()
|
||||||
=> GetString("TemplateRoute_InvalidRouteTemplate");
|
=> GetString("TemplateRoute_InvalidRouteTemplate");
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
|
||||||
Version 2.0
|
Version 2.0
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
The primary goals of this format is to allow a simple XML format
|
||||||
that is mostly human readable. The generation and parsing of the
|
that is mostly human readable. The generation and parsing of the
|
||||||
various data types are done through the TypeConverter classes
|
various data types are done through the TypeConverter classes
|
||||||
associated with the data types.
|
associated with the data types.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
... ado.net/XML headers & schema ...
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
<resheader name="version">2.0</resheader>
|
<resheader name="version">2.0</resheader>
|
||||||
|
|
@ -26,36 +26,36 @@
|
||||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
<comment>This is a comment</comment>
|
<comment>This is a comment</comment>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
There are any number of "resheader" rows that contain simple
|
||||||
name/value pairs.
|
name/value pairs.
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
Each data row contains a name, and value. The row also contains a
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
text/value conversion through the TypeConverter architecture.
|
text/value conversion through the TypeConverter architecture.
|
||||||
Classes that don't support this are serialized and stored with the
|
Classes that don't support this are serialized and stored with the
|
||||||
mimetype set.
|
mimetype set.
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
The mimetype is used for serialized objects, and tells the
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
read any of the formats listed below.
|
read any of the formats listed below.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
value : The object must be serialized into a byte array
|
value : The object must be serialized into a byte array
|
||||||
: using a System.ComponentModel.TypeConverter
|
: using a System.ComponentModel.TypeConverter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
|
|
@ -169,7 +169,7 @@
|
||||||
<value>The route parameter name '{0}' is invalid. Route parameter names must be non-empty and cannot contain these characters: '{{', '}}', '/'. The '?' character marks a parameter as optional, and can occur only at the end of the parameter. The '*' character marks a parameter as catch-all, and can occur only at the start of the parameter.</value>
|
<value>The route parameter name '{0}' is invalid. Route parameter names must be non-empty and cannot contain these characters: '{{', '}}', '/'. The '?' character marks a parameter as optional, and can occur only at the end of the parameter. The '*' character marks a parameter as catch-all, and can occur only at the start of the parameter.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TemplateRoute_InvalidRouteTemplate" xml:space="preserve">
|
<data name="TemplateRoute_InvalidRouteTemplate" xml:space="preserve">
|
||||||
<value>The route template cannot start with a '/' or '~' character.</value>
|
<value>The route template cannot start with a '~' character unless followed by a '/'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TemplateRoute_MismatchedParameter" xml:space="preserve">
|
<data name="TemplateRoute_MismatchedParameter" xml:space="preserve">
|
||||||
<value>There is an incomplete parameter in the route template. Check that each '{' character has a matching '}' character.</value>
|
<value>There is an incomplete parameter in the route template. Check that each '{' character has a matching '}' character.</value>
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,9 @@ namespace Microsoft.AspNetCore.Routing.Template
|
||||||
routeTemplate = String.Empty;
|
routeTemplate = String.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsInvalidRouteTemplate(routeTemplate))
|
var trimmedRouteTemplate = TrimPrefix(routeTemplate);
|
||||||
{
|
|
||||||
throw new ArgumentException(Resources.TemplateRoute_InvalidRouteTemplate, nameof(routeTemplate));
|
|
||||||
}
|
|
||||||
|
|
||||||
var context = new TemplateParserContext(routeTemplate);
|
var context = new TemplateParserContext(trimmedRouteTemplate);
|
||||||
var segments = new List<TemplateSegment>();
|
var segments = new List<TemplateSegment>();
|
||||||
|
|
||||||
while (context.Next())
|
while (context.Next())
|
||||||
|
|
@ -61,6 +58,23 @@ namespace Microsoft.AspNetCore.Routing.Template
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string TrimPrefix(string routeTemplate)
|
||||||
|
{
|
||||||
|
if (routeTemplate.StartsWith("~/", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return routeTemplate.Substring(2);
|
||||||
|
}
|
||||||
|
else if (routeTemplate.StartsWith("/", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return routeTemplate.Substring(1);
|
||||||
|
}
|
||||||
|
else if (routeTemplate.StartsWith("~", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(Resources.TemplateRoute_InvalidRouteTemplate, nameof(routeTemplate));
|
||||||
|
}
|
||||||
|
return routeTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool ParseSegment(TemplateParserContext context, List<TemplateSegment> segments)
|
private static bool ParseSegment(TemplateParserContext context, List<TemplateSegment> segments)
|
||||||
{
|
{
|
||||||
Debug.Assert(context != null);
|
Debug.Assert(context != null);
|
||||||
|
|
|
||||||
|
|
@ -691,6 +691,28 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
||||||
"/UrlEncode[[v1]]");
|
"/UrlEncode[[v1]]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetUrlWithLeadingTildeSlash()
|
||||||
|
{
|
||||||
|
RunTest(
|
||||||
|
"~/foo",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
new RouteValueDictionary(new { }),
|
||||||
|
"/UrlEncode[[foo]]");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetUrlWithLeadingSlash()
|
||||||
|
{
|
||||||
|
RunTest(
|
||||||
|
"/foo",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
new RouteValueDictionary(new { }),
|
||||||
|
"/UrlEncode[[foo]]");
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TemplateBinder_KeepsExplicitlySuppliedRouteValues_OnFailedRouetMatch()
|
public void TemplateBinder_KeepsExplicitlySuppliedRouteValues_OnFailedRouetMatch()
|
||||||
{
|
{
|
||||||
|
|
@ -721,7 +743,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
||||||
|
|
||||||
#if ROUTE_COLLECTION
|
#if ROUTE_COLLECTION
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetUrlShouldValidateOnlyAcceptedParametersAndUserDefaultValuesForInvalidatedParameters()
|
public void GetUrlShouldValidateOnlyAcceptedParametersAndUserDefaultValuesForInvalidatedParameters()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -755,7 +777,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
||||||
Assert.Equal<string>("/app1/UrlConstraints/Validation.mvc/Input5/MissmatchedValidateParameters2/valid1", vpd.VirtualPath);
|
Assert.Equal<string>("/app1/UrlConstraints/Validation.mvc/Input5/MissmatchedValidateParameters2/valid1", vpd.VirtualPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetUrlWithRouteThatHasExtensionWithSubsequentDefaultValueIncludesExtensionButNotDefaultValue()
|
public void GetUrlWithRouteThatHasExtensionWithSubsequentDefaultValueIncludesExtensionButNotDefaultValue()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
|
||||||
|
|
@ -655,6 +655,18 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
||||||
"Parameter name: routeTemplate");
|
"Parameter name: routeTemplate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("/foo")]
|
||||||
|
[InlineData("~/foo")]
|
||||||
|
public void ValidTemplate_CanStartWithSlashOrTildeSlash(string routeTemplate)
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var template = TemplateParser.Parse(routeTemplate);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(routeTemplate, template.TemplateText);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void InvalidTemplate_CannotHaveConsecutiveOpenBrace()
|
public void InvalidTemplate_CannotHaveConsecutiveOpenBrace()
|
||||||
{
|
{
|
||||||
|
|
@ -768,21 +780,12 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
||||||
"Parameter name: routeTemplate");
|
"Parameter name: routeTemplate");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void InvalidTemplate_CannotStartWithSlash()
|
|
||||||
{
|
|
||||||
ExceptionAssert.Throws<ArgumentException>(
|
|
||||||
() => TemplateParser.Parse("/foo"),
|
|
||||||
"The route template cannot start with a '/' or '~' character." + Environment.NewLine +
|
|
||||||
"Parameter name: routeTemplate");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void InvalidTemplate_CannotStartWithTilde()
|
public void InvalidTemplate_CannotStartWithTilde()
|
||||||
{
|
{
|
||||||
ExceptionAssert.Throws<ArgumentException>(
|
ExceptionAssert.Throws<ArgumentException>(
|
||||||
() => TemplateParser.Parse("~foo"),
|
() => TemplateParser.Parse("~foo"),
|
||||||
"The route template cannot start with a '/' or '~' character." + Environment.NewLine +
|
"The route template cannot start with a '~' character unless followed by a '/'." + Environment.NewLine +
|
||||||
"Parameter name: routeTemplate");
|
"Parameter name: routeTemplate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue