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);
|
||||
|
||||
/// <summary>
|
||||
/// The route template cannot start with a '/' or '~' character.
|
||||
/// The route template cannot start with a '~' character unless followed by a '/'.
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_InvalidRouteTemplate
|
||||
{
|
||||
|
|
@ -257,7 +257,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// The route template cannot start with a '/' or '~' character.
|
||||
/// The route template cannot start with a '~' character unless followed by a '/'.
|
||||
/// </summary>
|
||||
internal static string FormatTemplateRoute_InvalidRouteTemplate()
|
||||
=> GetString("TemplateRoute_InvalidRouteTemplate");
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</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>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
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
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
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
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
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
|
||||
: 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>
|
||||
</data>
|
||||
<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 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>
|
||||
|
|
|
|||
|
|
@ -25,12 +25,9 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
routeTemplate = String.Empty;
|
||||
}
|
||||
|
||||
if (IsInvalidRouteTemplate(routeTemplate))
|
||||
{
|
||||
throw new ArgumentException(Resources.TemplateRoute_InvalidRouteTemplate, nameof(routeTemplate));
|
||||
}
|
||||
var trimmedRouteTemplate = TrimPrefix(routeTemplate);
|
||||
|
||||
var context = new TemplateParserContext(routeTemplate);
|
||||
var context = new TemplateParserContext(trimmedRouteTemplate);
|
||||
var segments = new List<TemplateSegment>();
|
||||
|
||||
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)
|
||||
{
|
||||
Debug.Assert(context != null);
|
||||
|
|
|
|||
|
|
@ -691,6 +691,28 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
|||
"/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]
|
||||
public void TemplateBinder_KeepsExplicitlySuppliedRouteValues_OnFailedRouetMatch()
|
||||
{
|
||||
|
|
@ -721,7 +743,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
|||
|
||||
#if ROUTE_COLLECTION
|
||||
|
||||
[Fact]
|
||||
[Fact]
|
||||
public void GetUrlShouldValidateOnlyAcceptedParametersAndUserDefaultValuesForInvalidatedParameters()
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -755,7 +777,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
|||
Assert.Equal<string>("/app1/UrlConstraints/Validation.mvc/Input5/MissmatchedValidateParameters2/valid1", vpd.VirtualPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Fact]
|
||||
public void GetUrlWithRouteThatHasExtensionWithSubsequentDefaultValueIncludesExtensionButNotDefaultValue()
|
||||
{
|
||||
// Arrange
|
||||
|
|
|
|||
|
|
@ -655,6 +655,18 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
|||
"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]
|
||||
public void InvalidTemplate_CannotHaveConsecutiveOpenBrace()
|
||||
{
|
||||
|
|
@ -768,21 +780,12 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
|||
"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]
|
||||
public void InvalidTemplate_CannotStartWithTilde()
|
||||
{
|
||||
ExceptionAssert.Throws<ArgumentException>(
|
||||
() => 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");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue