Skip to content
This repository was archived by the owner on Nov 27, 2018. It is now read-only.

Commit ffa0ee5

Browse files
committed
[Fixes #324] Default parameters are not taken into account on attribute routing
1 parent a1ee754 commit ffa0ee5

File tree

2 files changed

+149
-1
lines changed

2 files changed

+149
-1
lines changed

src/Microsoft.AspNetCore.Routing/Tree/TreeRouteBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ private void AddEntryToTree(UrlMatchingTree tree, InboundRouteEntry entry)
333333
continue;
334334
}
335335

336-
if (part.IsParameter && (part.IsOptional || part.IsCatchAll))
336+
if (part.IsParameter && (part.IsOptional || part.IsCatchAll || part.DefaultValue != null))
337337
{
338338
current.Matches.Add(new InboundMatch() { Entry = entry, TemplateMatcher = matcher });
339339
}

test/Microsoft.AspNetCore.Routing.Tests/Tree/TreeRouterTest.cs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using Microsoft.Extensions.WebEncoders.Testing;
1717
using Moq;
1818
using Xunit;
19+
using Xunit.Extensions;
1920

2021
namespace Microsoft.AspNetCore.Routing.Tree
2122
{
@@ -125,6 +126,153 @@ public async Task TreeRouter_RouteAsync_MatchesRouteWithTheRightLength(string ur
125126
Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]);
126127
}
127128

129+
public static TheoryData<string, object[]> MatchesRoutesWithDefaultsData =>
130+
new TheoryData<string, object[]>
131+
{
132+
{ "/", new object[] { "1", "2", "3", "4" } },
133+
{ "/a", new object[] { "a", "2", "3", "4" } },
134+
{ "/a/b", new object[] { "a", "b", "3", "4" } },
135+
{ "/a/b/c", new object[] { "a", "b", "c", "4" } },
136+
{ "/a/b/c/d", new object[] { "a", "b", "c", "d" } }
137+
};
138+
139+
[Theory]
140+
[MemberData(nameof(MatchesRoutesWithDefaultsData))]
141+
public async Task TreeRouter_RouteAsync_MatchesRoutesWithDefaults(string url, object[] routeValues)
142+
{
143+
// Arrange
144+
var routes = new[] {
145+
"{parameter1=1}/{parameter2=2}/{parameter3=3}/{parameter4=4}",
146+
};
147+
148+
var expectedRouteGroup = CreateRouteGroup(0, "{parameter1=1}/{parameter2=2}/{parameter3=3}/{parameter4=4}");
149+
var routeValueKeys = new[] { "parameter1", "parameter2", "parameter3", "parameter4" };
150+
var expectedRouteValues = new RouteValueDictionary();
151+
for (int i = 0; i < routeValueKeys.Length; i++)
152+
{
153+
expectedRouteValues.Add(routeValueKeys[i], routeValues[i]);
154+
}
155+
156+
var builder = CreateBuilder();
157+
158+
// We setup the route entries in reverse order of precedence to ensure that when we
159+
// try to route the request, the route with a higher precedence gets tried first.
160+
foreach (var template in routes.Reverse())
161+
{
162+
MapInboundEntry(builder, template);
163+
}
164+
165+
var route = builder.Build();
166+
167+
var context = CreateRouteContext(url);
168+
169+
// Act
170+
await route.RouteAsync(context);
171+
172+
// Assert
173+
Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]);
174+
foreach (var entry in expectedRouteValues)
175+
{
176+
var data = Assert.Single(context.RouteData.Values, v => v.Key == entry.Key);
177+
Assert.Equal(entry.Value, data.Value);
178+
}
179+
}
180+
181+
public static TheoryData<string, object[]> MatchesConstrainedRoutesWithDefaultsData =>
182+
new TheoryData<string, object[]>
183+
{
184+
{ "/", new object[] { "1", "2", "3", "4" } },
185+
{ "/10", new object[] { "10", "2", "3", "4" } },
186+
{ "/10/11", new object[] { "10", "11", "3", "4" } },
187+
{ "/10/11/12", new object[] { "10", "11", "12", "4" } },
188+
{ "/10/11/12/13", new object[] { "10", "11", "12", "13" } }
189+
};
190+
191+
[Theory]
192+
[MemberData(nameof(MatchesConstrainedRoutesWithDefaultsData))]
193+
public async Task TreeRouter_RouteAsync_MatchesConstrainedRoutesWithDefaults(string url, object[] routeValues)
194+
{
195+
// Arrange
196+
var routes = new[] {
197+
"{parameter1:int=1}/{parameter2:int=2}/{parameter3:int=3}/{parameter4:int=4}",
198+
};
199+
200+
var expectedRouteGroup = CreateRouteGroup(0, "{parameter1:int=1}/{parameter2:int=2}/{parameter3:int=3}/{parameter4:int=4}");
201+
var routeValueKeys = new[] { "parameter1", "parameter2", "parameter3", "parameter4" };
202+
var expectedRouteValues = new RouteValueDictionary();
203+
for (int i = 0; i < routeValueKeys.Length; i++)
204+
{
205+
expectedRouteValues.Add(routeValueKeys[i], routeValues[i]);
206+
}
207+
208+
var builder = CreateBuilder();
209+
210+
// We setup the route entries in reverse order of precedence to ensure that when we
211+
// try to route the request, the route with a higher precedence gets tried first.
212+
foreach (var template in routes.Reverse())
213+
{
214+
MapInboundEntry(builder, template);
215+
}
216+
217+
var route = builder.Build();
218+
219+
var context = CreateRouteContext(url);
220+
221+
// Act
222+
await route.RouteAsync(context);
223+
224+
// Assert
225+
Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]);
226+
foreach (var entry in expectedRouteValues)
227+
{
228+
var data = Assert.Single(context.RouteData.Values, v => v.Key == entry.Key);
229+
Assert.Equal(entry.Value, data.Value);
230+
}
231+
}
232+
233+
[Fact]
234+
public async Task TreeRouter_RouteAsync_MatchesCatchAllRoutesWithDefaults()
235+
{
236+
// Arrange
237+
var routes = new[] {
238+
"{parameter1=1}/{parameter2=2}/{parameter3=3}/{*parameter4=4}",
239+
};
240+
var url = "/a/b/c";
241+
var routeValues = new[] { "a", "b", "c", "4" };
242+
243+
var expectedRouteGroup = CreateRouteGroup(0, "{parameter1=1}/{parameter2=2}/{parameter3=3}/{*parameter4=4}");
244+
var routeValueKeys = new[] { "parameter1", "parameter2", "parameter3", "parameter4" };
245+
var expectedRouteValues = new RouteValueDictionary();
246+
for (int i = 0; i < routeValueKeys.Length; i++)
247+
{
248+
expectedRouteValues.Add(routeValueKeys[i], routeValues[i]);
249+
}
250+
251+
var builder = CreateBuilder();
252+
253+
// We setup the route entries in reverse order of precedence to ensure that when we
254+
// try to route the request, the route with a higher precedence gets tried first.
255+
foreach (var template in routes.Reverse())
256+
{
257+
MapInboundEntry(builder, template);
258+
}
259+
260+
var route = builder.Build();
261+
262+
var context = CreateRouteContext(url);
263+
264+
// Act
265+
await route.RouteAsync(context);
266+
267+
// Assert
268+
Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]);
269+
foreach (var entry in expectedRouteValues)
270+
{
271+
var data = Assert.Single(context.RouteData.Values, v => v.Key == entry.Key);
272+
Assert.Equal(entry.Value, data.Value);
273+
}
274+
}
275+
128276
[Fact]
129277
public async Task TreeRouter_RouteAsync_DoesNotMatchShorterUrl()
130278
{

0 commit comments

Comments
 (0)