RedirectToRoute ignores default values for route when generating redirect URL.
Assuming the following route, which generates a url like /GtR5Rr/Question/1:
routes.MapRoute(
"Quiz",
"{urlId:regex((?i)[a-z0-9]{{6}})}/{action=Question}/{questionNumber?}",
defaults: new { controller = "Quiz" }
);
As you can see, the route value for action has a default value of Question. However when you try to generate a URL using this route using RedirectToRoute and you don't specify an action, like so:
return this.RedirectToRoute("Quiz", new { urlId = "GtR5Rr", questionNumber = 2 });
It seems as though the default values are being ignored, because I end up with a URL with the action of Start which is another action on my controller that isn't even mentioned in my route named "Quiz".
I'm getting this in the console output log:
Microsoft.AspNetCore.Mvc.RedirectToRouteResult:Information: Executing RedirectToRouteResult, redirecting to /MDZ0rn/Start/2 from route Quiz.
I'm converting my website from ASP.NET MVC to .NET Core and I'm having to add all of the default values where I didn't before. Is this the desired behaviour in the new .NET Core framework, or should it fill in the default route values?
When using RedirectToRoute, the default values should be filled in when they are not specified explicitly in the routeValues parameter.
So if there is a default value for action in the route with the value Question, then the URL that is generated when the action is omitted in the call to RedirectToRoute(), then it should take the default value. In this case it should generate the URL /MDZ0rn/Question/2.
ASP.NET Core project.
.NET Core SDK (reflecting any global.json):
Version: 2.2.103
Commit: 8edbc2570a
Runtime Environment:
OS Name: Windows
OS Version: 10.0.17763
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\2.2.103\
Host (useful for support):
Version: 2.2.1
Commit: 878dd11e62
.NET Core SDKs installed:
2.1.503 [C:\Program Files\dotnet\sdk]
2.1.504 [C:\Program Files\dotnet\sdk]
2.2.103 [C:\Program Files\dotnet\sdk]
.NET Core runtimes installed:
Microsoft.AspNetCore.All 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.2.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.2.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
To install additional .NET Core runtimes or SDKs:
https://aka.ms/dotnet-download
Thanks for contacting us, @coultonluke.
@JamesNK is the action getting the ambient value here which is Start? Is this the expected behavior?
Thanks for taking the time to respond. I have created a ASP.NET Core project to demonstrate the issue: https://github.com/coultonluke/aspnetcore-routing-issue
It's a new project with just the route and a single Quiz controller. If you visit https://localhost:5001/GtR5Rr/Question/1 it should redirect you to https://localhost:5001/GtR5Rr/Question/2 because of the hard coded question number (the behavior is to redirect a user to the correct question), but it redirects to the Start action instead.
routes.MapRoute(
"Quiz",
"{urlId:regex((?i)[a-z0-9]{{6}})}/{action=Question}/{questionNumber?}",
defaults: new { controller = "Quiz" }
)
public class QuizController : Controller
{
public async Task<IActionResult> Start()
{
return await Task.FromResult(this.View());
}
public async Task<IActionResult> Question(string urlId, int? questionNumber)
{
const int actualQuestionNumber = 2;
if (questionNumber != null && questionNumber.Value != actualQuestionNumber)
{
// Redirect to question 2
return await Task.FromResult(this.RedirectToRoute("Quiz", new { urlId = urlId, questionNumber = actualQuestionNumber }));
}
return await Task.FromResult(this.View());
}
}
I don't understand why it would be taking the Start action name when the URL is being generated in the Question action and not Start. Shouldn't the ambient action value be the name of the action that you are generating the redirect within or at least whatever the default value is specified within the route... especially as I'm supplying the precise route name to generate a redirect for?
It's taking the name of the first action on the controller as the default value when generating the redirect, when what I expect is for it to use the default values from the route. Otherwise the default values are only useful for when visiting a route and and not route generation.
Hi @coultonluke
I can replicate your issue. I get the correct behavior (what you expect to happen) if endpoint routing is disabled in the Startup.cs: services.AddMvc(options => options.EnableEndpointRouting = false). For now I would recommend you disable endpoint routing as a work-around.
I'll investigate further to see whether this is an issue that is fixed in a 2.2 patch or 3.0.
I've confirmed that it is still broken in the latest version of 2.2
I've also confirmed that it works correctly in 3.0 with endpoint routing.
Because there is a simple workaround in 2.2 - disable endpoint routing - I recommend you do that.
Thanks for taking the time. I'll turn that option off for now until I upgrade to .NET Core 3.0.