Adding optional parameters
This commit is contained in:
parent
42ce8c6594
commit
64c29fe813
|
|
@ -0,0 +1,115 @@
|
|||
// <auto-generated />
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
internal static class Resources
|
||||
{
|
||||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.AspNet.Routing.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// A path segment that contains more than one section, such as a literal section or a parameter, cannot contain a catch-all parameter.
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_CannotHaveCatchAllInMultiSegment
|
||||
{
|
||||
get { return GetString("TemplateRoute_CannotHaveCatchAllInMultiSegment"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A path segment cannot contain two consecutive parameters. They must be separated by a '/' or by a literal string.
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_CannotHaveConsecutiveParameters
|
||||
{
|
||||
get { return GetString("TemplateRoute_CannotHaveConsecutiveParameters"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The route template separator character '/' cannot appear consecutively. It must be separated by either a parameter or a literal value.
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_CannotHaveConsecutiveSeparators
|
||||
{
|
||||
get { return GetString("TemplateRoute_CannotHaveConsecutiveSeparators"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A path segment that contains more than one section, such as a literal section or a parameter, cannot contain an optional parameter.
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_CannotHaveOptionalParameterInMultiSegment
|
||||
{
|
||||
get { return GetString("TemplateRoute_CannotHaveOptionalParameterInMultiSegment"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A catch-all parameter can only appear as the last segment of the route template.
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_CatchAllMustBeLast
|
||||
{
|
||||
get { return GetString("TemplateRoute_CatchAllMustBeLast"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The literal section '{0}' is invalid. Literal sections cannot contain the '?' character.
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_InvalidLiteral
|
||||
{
|
||||
get { return GetString("TemplateRoute_InvalidLiteral"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 only occur at the end of the parameter.
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_InvalidParameterName
|
||||
{
|
||||
get { return GetString("TemplateRoute_InvalidParameterName"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The route template cannot start with a '/' or '~' character.
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_InvalidRouteTemplate
|
||||
{
|
||||
get { return GetString("TemplateRoute_InvalidRouteTemplate"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// There is an incomplete parameter in the route template. Check that each '{' character has a matching '}' character.
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_MismatchedParameter
|
||||
{
|
||||
get { return GetString("TemplateRoute_MismatchedParameter"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The route parameter name '{0}' appears more than one time in the route template.
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_RepeatedParameter
|
||||
{
|
||||
get { return GetString("TemplateRoute_RepeatedParameter"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The constraint entry '{0}' on the route with route template '{1}' must have a string value or be of a type which implements '{2}'.
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_ValidationMustBeStringOrCustomConstraint
|
||||
{
|
||||
get { return GetString("TemplateRoute_ValidationMustBeStringOrCustomConstraint"); }
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] argumentNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
||||
System.Diagnostics.Debug.Assert(value != null);
|
||||
|
||||
for (var i = 0; i < argumentNames.Length; i++)
|
||||
{
|
||||
value = value.Replace("{" + argumentNames[i] + "}", "{" + i + "}");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.34003
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.AspNet.Routing {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
#if NET45
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNet.Routing.Resources", typeof(Resources).Assembly);
|
||||
#else
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNet.Routing.Resources", System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(Resources)).Assembly);
|
||||
#endif
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to A path segment that contains more than one section, such as a literal section or a parameter, cannot contain a catch-all parameter..
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_CannotHaveCatchAllInMultiSegment {
|
||||
get {
|
||||
return ResourceManager.GetString("TemplateRoute_CannotHaveCatchAllInMultiSegment", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to A path segment cannot contain two consecutive parameters. They must be separated by a '/' or by a literal string..
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_CannotHaveConsecutiveParameters {
|
||||
get {
|
||||
return ResourceManager.GetString("TemplateRoute_CannotHaveConsecutiveParameters", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The route template separator character '/' cannot appear consecutively. It must be separated by either a parameter or a literal value..
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_CannotHaveConsecutiveSeparators {
|
||||
get {
|
||||
return ResourceManager.GetString("TemplateRoute_CannotHaveConsecutiveSeparators", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to A catch-all parameter can only appear as the last segment of the route template..
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_CatchAllMustBeLast {
|
||||
get {
|
||||
return ResourceManager.GetString("TemplateRoute_CatchAllMustBeLast", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The route parameter name '{0}' is invalid. Route parameter names must be non-empty and cannot contain these characters: "{{", "}}", "/", "?".
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_InvalidParameterName {
|
||||
get {
|
||||
return ResourceManager.GetString("TemplateRoute_InvalidParameterName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The route template cannot start with a '/' or '~' character and it cannot contain a '?' character..
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_InvalidRouteTemplate {
|
||||
get {
|
||||
return ResourceManager.GetString("TemplateRoute_InvalidRouteTemplate", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to There is an incomplete parameter in the route template. Check that each '{' character has a matching '}' character..
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_MismatchedParameter {
|
||||
get {
|
||||
return ResourceManager.GetString("TemplateRoute_MismatchedParameter", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The route parameter name '{0}' appears more than one time in the route template..
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_RepeatedParameter {
|
||||
get {
|
||||
return ResourceManager.GetString("TemplateRoute_RepeatedParameter", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The constraint entry '{0}' on the route with route template '{1}' must have a string value or be of a type which implements '{2}'..
|
||||
/// </summary>
|
||||
internal static string TemplateRoute_ValidationMustBeStringOrCustomConstraint {
|
||||
get {
|
||||
return ResourceManager.GetString("TemplateRoute_ValidationMustBeStringOrCustomConstraint", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -126,14 +126,20 @@
|
|||
<data name="TemplateRoute_CannotHaveConsecutiveSeparators" xml:space="preserve">
|
||||
<value>The route template separator character '/' cannot appear consecutively. It must be separated by either a parameter or a literal value.</value>
|
||||
</data>
|
||||
<data name="TemplateRoute_CannotHaveOptionalParameterInMultiSegment" xml:space="preserve">
|
||||
<value>A path segment that contains more than one section, such as a literal section or a parameter, cannot contain an optional parameter.</value>
|
||||
</data>
|
||||
<data name="TemplateRoute_CatchAllMustBeLast" xml:space="preserve">
|
||||
<value>A catch-all parameter can only appear as the last segment of the route template.</value>
|
||||
</data>
|
||||
<data name="TemplateRoute_InvalidLiteral" xml:space="preserve">
|
||||
<value>The literal section '{0}' is invalid. Literal sections cannot contain the '?' character.</value>
|
||||
</data>
|
||||
<data name="TemplateRoute_InvalidParameterName" xml:space="preserve">
|
||||
<value>The route parameter name '{0}' is invalid. Route parameter names must be non-empty and cannot contain these characters: "{{", "}}", "/", "?"</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 only occur at the end of the parameter.</value>
|
||||
</data>
|
||||
<data name="TemplateRoute_InvalidRouteTemplate" xml:space="preserve">
|
||||
<value>The route template cannot start with a '/' or '~' character and it cannot contain a '?' character.</value>
|
||||
<value>The route template cannot start with a '/' or '~' character.</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>
|
||||
|
|
|
|||
|
|
@ -144,6 +144,10 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
{
|
||||
values.Add(part.Name, defaultValue);
|
||||
}
|
||||
else if (part.IsOptional)
|
||||
{
|
||||
// This is optional (with no default value) - there's nothing to capture here, so just move on.
|
||||
}
|
||||
else
|
||||
{
|
||||
// There's no default for this (non-catch-all) parameter so it can't match.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
private const char Separator = '/';
|
||||
private const char OpenBrace = '{';
|
||||
private const char CloseBrace = '}';
|
||||
private const char EqualsSign = '=';
|
||||
private const char QuestionMark = '?';
|
||||
|
||||
public static ParsedTemplate Parse(string routeTemplate)
|
||||
{
|
||||
|
|
@ -174,10 +176,15 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
var rawName = context.Capture();
|
||||
|
||||
var isCatchAll = rawName.StartsWith("*", StringComparison.Ordinal);
|
||||
var parameterName = isCatchAll ? rawName.Substring(1) : rawName;
|
||||
var isOptional = rawName.EndsWith("?", StringComparison.Ordinal);
|
||||
|
||||
rawName = isCatchAll ? rawName.Substring(1) : rawName;
|
||||
rawName = isOptional ? rawName.Substring(0, rawName.Length - 1) : rawName;
|
||||
|
||||
var parameterName = rawName;
|
||||
if (IsValidParameterName(context, parameterName))
|
||||
{
|
||||
segment.Parts.Add(TemplatePart.CreateParameter(parameterName, isCatchAll));
|
||||
segment.Parts.Add(TemplatePart.CreateParameter(parameterName, isCatchAll, isOptional));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
|
@ -250,8 +257,15 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
}
|
||||
|
||||
var decoded = encoded.Replace("}}", "}").Replace("{{", "}");
|
||||
segment.Parts.Add(TemplatePart.CreateLiteral(decoded));
|
||||
return true;
|
||||
if (IsValidLiteral(context, decoded))
|
||||
{
|
||||
segment.Parts.Add(TemplatePart.CreateLiteral(decoded));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsAllValid(TemplateParserContext context, List<TemplateSegment> segments)
|
||||
|
|
@ -287,6 +301,17 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
}
|
||||
}
|
||||
|
||||
// if a segment has multiple parts, then the parameters can't be optional
|
||||
for (int i = 0; i < segment.Parts.Count; i++)
|
||||
{
|
||||
var part = segment.Parts[i];
|
||||
if (part.IsParameter && part.IsOptional && segment.Parts.Count > 1)
|
||||
{
|
||||
context.Error = Resources.TemplateRoute_CannotHaveOptionalParameterInMultiSegment;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// A segment cannot containt two consecutive parameters
|
||||
var isLastSegmentParameter = false;
|
||||
for (int i = 0; i < segment.Parts.Count; i++)
|
||||
|
|
@ -315,7 +340,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
for (int i = 0; i < parameterName.Length; i++)
|
||||
{
|
||||
var c = parameterName[i];
|
||||
if (c == '/' || c == '{' || c == '}')
|
||||
if (c == Separator || c == OpenBrace || c == CloseBrace || c == QuestionMark)
|
||||
{
|
||||
context.Error = String.Format(CultureInfo.CurrentCulture, Resources.TemplateRoute_InvalidParameterName, parameterName);
|
||||
return false;
|
||||
|
|
@ -331,11 +356,24 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
return true;
|
||||
}
|
||||
|
||||
private static bool IsValidLiteral(TemplateParserContext context, string literal)
|
||||
{
|
||||
Contract.Assert(context != null);
|
||||
Contract.Assert(literal != null);
|
||||
|
||||
if (literal.IndexOf(QuestionMark) != -1)
|
||||
{
|
||||
context.Error = String.Format(CultureInfo.CurrentCulture, Resources.TemplateRoute_InvalidLiteral, literal);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsInvalidRouteTemplate(string routeTemplate)
|
||||
{
|
||||
return routeTemplate.StartsWith("~", StringComparison.Ordinal) ||
|
||||
routeTemplate.StartsWith("/", StringComparison.Ordinal) ||
|
||||
(routeTemplate.IndexOf('?') != -1);
|
||||
routeTemplate.StartsWith("/", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -16,20 +16,21 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
};
|
||||
}
|
||||
|
||||
public static TemplatePart CreateParameter(string name, bool isCatchAll)
|
||||
public static TemplatePart CreateParameter(string name, bool isCatchAll, bool isOptional)
|
||||
{
|
||||
return new TemplatePart()
|
||||
{
|
||||
IsParameter = true,
|
||||
Name = name,
|
||||
IsCatchAll = isCatchAll,
|
||||
IsOptional = isOptional,
|
||||
};
|
||||
}
|
||||
|
||||
public bool IsCatchAll { get; private set; }
|
||||
public bool IsLiteral { get; private set; }
|
||||
public bool IsParameter { get; private set; }
|
||||
|
||||
public bool IsOptional { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public string Text { get; private set; }
|
||||
|
||||
|
|
@ -37,7 +38,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
{
|
||||
if (IsParameter)
|
||||
{
|
||||
return "{" + (IsCatchAll ? "*" : string.Empty) + Name + "}";
|
||||
return "{" + (IsCatchAll ? "*" : string.Empty) + Name + (IsOptional ? "?" : string.Empty) + "}";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -35,7 +35,24 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
|
||||
var expected = new ParsedTemplate(new List<TemplateSegment>());
|
||||
expected.Segments.Add(new TemplateSegment());
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateParameter("p", false));
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateParameter("p", false, false));
|
||||
|
||||
// Act
|
||||
var actual = TemplateParser.Parse(template);
|
||||
|
||||
// Assert
|
||||
Assert.Equal<ParsedTemplate>(expected, actual, new TemplateParsedRouteEqualityComparer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_OptionalParameter()
|
||||
{
|
||||
// Arrange
|
||||
var template = "{p?}";
|
||||
|
||||
var expected = new ParsedTemplate(new List<TemplateSegment>());
|
||||
expected.Segments.Add(new TemplateSegment());
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateParameter("p", false, true));
|
||||
|
||||
// Act
|
||||
var actual = TemplateParser.Parse(template);
|
||||
|
|
@ -73,11 +90,11 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
|
||||
var expected = new ParsedTemplate(new List<TemplateSegment>());
|
||||
expected.Segments.Add(new TemplateSegment());
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateParameter("p1", false));
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateParameter("p1", false, false));
|
||||
expected.Segments.Add(new TemplateSegment());
|
||||
expected.Segments[1].Parts.Add(TemplatePart.CreateParameter("p2", false));
|
||||
expected.Segments[1].Parts.Add(TemplatePart.CreateParameter("p2", false, false));
|
||||
expected.Segments.Add(new TemplateSegment());
|
||||
expected.Segments[2].Parts.Add(TemplatePart.CreateParameter("p3", true));
|
||||
expected.Segments[2].Parts.Add(TemplatePart.CreateParameter("p3", true, false));
|
||||
|
||||
// Act
|
||||
var actual = TemplateParser.Parse(template);
|
||||
|
|
@ -95,7 +112,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
var expected = new ParsedTemplate(new List<TemplateSegment>());
|
||||
expected.Segments.Add(new TemplateSegment());
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateLiteral("cool-"));
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateParameter("p1", false));
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateParameter("p1", false, false));
|
||||
|
||||
// Act
|
||||
var actual = TemplateParser.Parse(template);
|
||||
|
|
@ -112,7 +129,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
|
||||
var expected = new ParsedTemplate(new List<TemplateSegment>());
|
||||
expected.Segments.Add(new TemplateSegment());
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateParameter("p1", false));
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateParameter("p1", false, false));
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateLiteral("cool-"));
|
||||
|
||||
// Act
|
||||
|
|
@ -130,9 +147,9 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
|
||||
var expected = new ParsedTemplate(new List<TemplateSegment>());
|
||||
expected.Segments.Add(new TemplateSegment());
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateParameter("p1", false));
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateParameter("p1", false, false));
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateLiteral("cool-"));
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateParameter("p2", false));
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateParameter("p2", false, false));
|
||||
|
||||
// Act
|
||||
var actual = TemplateParser.Parse(template);
|
||||
|
|
@ -150,7 +167,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
var expected = new ParsedTemplate(new List<TemplateSegment>());
|
||||
expected.Segments.Add(new TemplateSegment());
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateLiteral("cool-"));
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateParameter("p1", false));
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateParameter("p1", false, false));
|
||||
expected.Segments[0].Parts.Add(TemplatePart.CreateLiteral("-awesome"));
|
||||
|
||||
// Act
|
||||
|
|
@ -215,7 +232,8 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
{
|
||||
Assert.Throws<ArgumentException>(
|
||||
() => TemplateParser.Parse("foo/{*}"),
|
||||
@"The route parameter name '' is invalid. Route parameter names must be non-empty and cannot contain these characters: ""{"", ""}"", ""/"", ""?""" + Environment.NewLine +
|
||||
"The route parameter name '' is invalid. Route parameter names must be non-empty and cannot contain these characters: '{', '}', '/'. " +
|
||||
"The '?' character marks a parameter as optional, and can only occur at the end of the parameter." + Environment.NewLine +
|
||||
"Parameter name: routeTemplate");
|
||||
}
|
||||
|
||||
|
|
@ -269,7 +287,8 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
{
|
||||
Assert.Throws<ArgumentException>(
|
||||
() => TemplateParser.Parse("{a}/{a{aa}/{z}"),
|
||||
@"The route parameter name 'a{aa' is invalid. Route parameter names must be non-empty and cannot contain these characters: ""{"", ""}"", ""/"", ""?""" + Environment.NewLine +
|
||||
"The route parameter name 'a{aa' is invalid. Route parameter names must be non-empty and cannot contain these characters: '{', '}', '/'. " +
|
||||
"The '?' character marks a parameter as optional, and can only occur at the end of the parameter." + Environment.NewLine +
|
||||
"Parameter name: routeTemplate");
|
||||
}
|
||||
|
||||
|
|
@ -278,7 +297,8 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
{
|
||||
Assert.Throws<ArgumentException>(
|
||||
() => TemplateParser.Parse("{a}/{}/{z}"),
|
||||
@"The route parameter name '' is invalid. Route parameter names must be non-empty and cannot contain these characters: ""{"", ""}"", ""/"", ""?""" + Environment.NewLine +
|
||||
"The route parameter name '' is invalid. Route parameter names must be non-empty and cannot contain these characters: '{', '}', '/'. " +
|
||||
"The '?' character marks a parameter as optional, and can only occur at the end of the parameter." + Environment.NewLine +
|
||||
"Parameter name: routeTemplate");
|
||||
}
|
||||
|
||||
|
|
@ -287,7 +307,8 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
{
|
||||
Assert.Throws<ArgumentException>(
|
||||
() => TemplateParser.Parse("{Controller}.mvc/{?}"),
|
||||
"The route template cannot start with a '/' or '~' character and it cannot contain a '?' character." + Environment.NewLine +
|
||||
"The route parameter name '' is invalid. Route parameter names must be non-empty and cannot contain these characters: '{', '}', '/'. " +
|
||||
"The '?' character marks a parameter as optional, and can only occur at the end of the parameter." + Environment.NewLine +
|
||||
"Parameter name: routeTemplate");
|
||||
}
|
||||
|
||||
|
|
@ -323,7 +344,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
{
|
||||
Assert.Throws<ArgumentException>(
|
||||
() => TemplateParser.Parse("/foo"),
|
||||
"The route template cannot start with a '/' or '~' character and it cannot contain a '?' character." + Environment.NewLine +
|
||||
"The route template cannot start with a '/' or '~' character." + Environment.NewLine +
|
||||
"Parameter name: routeTemplate");
|
||||
}
|
||||
|
||||
|
|
@ -332,7 +353,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
{
|
||||
Assert.Throws<ArgumentException>(
|
||||
() => TemplateParser.Parse("~foo"),
|
||||
"The route template cannot start with a '/' or '~' character and it cannot contain a '?' character." + Environment.NewLine +
|
||||
"The route template cannot start with a '/' or '~' character." + Environment.NewLine +
|
||||
"Parameter name: routeTemplate");
|
||||
}
|
||||
|
||||
|
|
@ -341,10 +362,29 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
{
|
||||
Assert.Throws<ArgumentException>(
|
||||
() => TemplateParser.Parse("foor?bar"),
|
||||
"The route template cannot start with a '/' or '~' character and it cannot contain a '?' character." + Environment.NewLine +
|
||||
"The literal section 'foor?bar' is invalid. Literal sections cannot contain the '?' character." + Environment.NewLine +
|
||||
"Parameter name: routeTemplate");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidTemplate_ParameterCannotContainQuestionMark_UnlessAtEnd()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(
|
||||
() => TemplateParser.Parse("{foor?b}"),
|
||||
"The route parameter name 'foor?b' is invalid. Route parameter names must be non-empty and cannot contain these characters: '{', '}', '/'. " +
|
||||
"The '?' character marks a parameter as optional, and can only occur at the end of the parameter." + Environment.NewLine +
|
||||
"Parameter name: routeTemplate");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidTemplate_MultiSegmentParameterCannotContainOptionalParameter()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(
|
||||
() => TemplateParser.Parse("{foorb?}-bar-{z}"),
|
||||
"A path segment that contains more than one section, such as a literal section or a parameter, cannot contain an optional parameter." + Environment.NewLine +
|
||||
"Parameter name: routeTemplate");
|
||||
}
|
||||
|
||||
private class TemplateParsedRouteEqualityComparer : IEqualityComparer<ParsedTemplate>
|
||||
{
|
||||
public bool Equals(ParsedTemplate x, ParsedTemplate y)
|
||||
|
|
@ -379,6 +419,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
if (xPart.IsLiteral != yPart.IsLiteral ||
|
||||
xPart.IsParameter != yPart.IsParameter ||
|
||||
xPart.IsCatchAll != yPart.IsCatchAll ||
|
||||
xPart.IsOptional != yPart.IsOptional ||
|
||||
!String.Equals(xPart.Name, yPart.Name, StringComparison.Ordinal) ||
|
||||
!String.Equals(xPart.Name, yPart.Name, StringComparison.Ordinal))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -710,6 +710,52 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
new RouteValueDictionary { { "language", "xx" }, { "locale", "yy" }, { "controller", "foo" } });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchSetsOptionalParameter()
|
||||
{
|
||||
// Arrange
|
||||
var route = CreateRoute("{controller}/{action?}");
|
||||
var url = "Home/Index";
|
||||
|
||||
// Act
|
||||
var match = route.Match(new RouteContext(GetHttpContext(url)));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(match);
|
||||
Assert.Equal("Index", match.Values["action"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchDoesNotSetOptionalParameter()
|
||||
{
|
||||
// Arrange
|
||||
var route = CreateRoute("{controller}/{action?}");
|
||||
var url = "Home";
|
||||
|
||||
// Act
|
||||
var match = route.Match(new RouteContext(GetHttpContext(url)));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(match);
|
||||
Assert.False(match.Values.ContainsKey("action"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchMultipleOptionalParameters()
|
||||
{
|
||||
// Arrange
|
||||
var route = CreateRoute("{controller}/{action?}/{id?}");
|
||||
var url = "Home/Index";
|
||||
|
||||
// Act
|
||||
var match = route.Match(new RouteContext(GetHttpContext(url)));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(match);
|
||||
Assert.Equal("Index", match.Values["action"]);
|
||||
Assert.False(match.Values.ContainsKey("id"));
|
||||
}
|
||||
|
||||
private static IRouteValues CreateRouteData()
|
||||
{
|
||||
return new RouteValues(new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase));
|
||||
|
|
|
|||
Loading…
Reference in New Issue