From 1c7f53ae39f28384438122e44c72c9edc8ef857d Mon Sep 17 00:00:00 2001 From: Kiran Challa Date: Thu, 12 Jul 2018 16:00:51 -0700 Subject: [PATCH] Fix TemplateBinder to consider null and empty string values the same --- .../Template/TemplateBinder.cs | 8 ++- .../Template/TemplateBinderTests.cs | 62 ++++++++++++++++++- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.AspNetCore.Routing/Template/TemplateBinder.cs b/src/Microsoft.AspNetCore.Routing/Template/TemplateBinder.cs index 802352e935..241705523b 100644 --- a/src/Microsoft.AspNetCore.Routing/Template/TemplateBinder.cs +++ b/src/Microsoft.AspNetCore.Routing/Template/TemplateBinder.cs @@ -375,7 +375,13 @@ namespace Microsoft.AspNetCore.Routing.Template var sa = a as string; var sb = b as string; - if (sa != null && sb != null) + // In case of strings, consider empty and null the same. + // Since null cannot tell us the type, consider it to be a string if the other value is a string. + if ((sa == string.Empty && sb == null) || (sb == string.Empty && sa == null)) + { + return true; + } + else if (sa != null && sb != null) { // For strings do a case-insensitive comparison return string.Equals(sa, sb, StringComparison.OrdinalIgnoreCase); diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Template/TemplateBinderTests.cs b/test/Microsoft.AspNetCore.Routing.Tests/Template/TemplateBinderTests.cs index d4dacaaa6b..c5fd2e7b9e 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Template/TemplateBinderTests.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Template/TemplateBinderTests.cs @@ -708,7 +708,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests } [Fact] - public void TemplateBinder_KeepsExplicitlySuppliedRouteValues_OnFailedRouetMatch() + public void TemplateBinder_KeepsExplicitlySuppliedRouteValues_OnFailedRouteMatch() { // Arrange var template = "{area?}/{controller=Home}/{action=Index}/{id?}"; @@ -1211,6 +1211,8 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests [Theory] [InlineData(null, null, true)] + [InlineData("", null, true)] + [InlineData(null, "", true)] [InlineData("blog", null, false)] [InlineData(null, "store", false)] [InlineData("Cool", "cool", true)] @@ -1228,6 +1230,64 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests } } + [Fact] + public void GetValues_SuccessfullyMatchesRouteValues_ForExplicitEmptyStringValue_AndNullDefault() + { + // Arrange + var expected = "/Home/Index"; + var template = "Home/Index"; + var defaults = new RouteValueDictionary(new { controller = "Home", action = "Index", area = (string)null }); + var ambientValues = new RouteValueDictionary(new { controller = "Rail", action = "Schedule", area = "Travel" }); + var explicitValues = new RouteValueDictionary(new { controller = "Home", action = "Index", area = "" }); + var binder = new TemplateBinder( + UrlEncoder.Default, + new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()), + TemplateParser.Parse(template), + defaults); + + // Act1 + var result = binder.GetValues(ambientValues, explicitValues); + + // Assert1 + Assert.NotNull(result); + + // Act2 + var boundTemplate = binder.BindValues(result.AcceptedValues); + + // Assert2 + Assert.NotNull(boundTemplate); + Assert.Equal(expected, boundTemplate); + } + + [Fact] + public void GetValues_SuccessfullyMatchesRouteValues_ForExplicitNullValue_AndEmptyStringDefault() + { + // Arrange + var expected = "/Home/Index"; + var template = "Home/Index"; + var defaults = new RouteValueDictionary(new { controller = "Home", action = "Index", area = "" }); + var ambientValues = new RouteValueDictionary(new { controller = "Rail", action = "Schedule", area = "Travel" }); + var explicitValues = new RouteValueDictionary(new { controller = "Home", action = "Index", area = (string)null }); + var binder = new TemplateBinder( + UrlEncoder.Default, + new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()), + TemplateParser.Parse(template), + defaults); + + // Act1 + var result = binder.GetValues(ambientValues, explicitValues); + + // Assert1 + Assert.NotNull(result); + + // Act2 + var boundTemplate = binder.BindValues(result.AcceptedValues); + + // Assert2 + Assert.NotNull(boundTemplate); + Assert.Equal(expected, boundTemplate); + } + private static IInlineConstraintResolver GetInlineConstraintResolver() { var services = new ServiceCollection().AddOptions();