Search code examples
c#asp.net-mvcasp.net-coreasp.net-mvc-routing

Url.Action without parameter appends one if the page is visited from another link that has parameter


I have a menu, from which I set a link to a page without parameters:

<a href='@Url.Action("DaysOffSchedule", "DaysOff")'>My schedule</a>

On another page, I have a list of items with links that provide an id:

<a href='@Url.Action("DaysOffSchedule", "DaysOff", new { personnelId = 2 })'>Smith's schedule</a>
<a href='@Url.Action("DaysOffSchedule", "DaysOff", new { personnelId = 7 })'>Doe's schedule</a>

Once I visit Smith's schedule and do a post back to save their data, my menu link automatically appends 2 and becomes <a href="/DaysOff/DaysOffSchedule/2">My schedule</a>

Here is my route mapping:

app.MapControllerRoute(
    name: "days-off-schedule",
    pattern: $"DaysOff/DaysOffSchedule/{{personnelId}}", // with or without ? doesn't affect the behaviour
    defaults: new {controller = "DaysOff", action = "DaysOffSchedule" });
app.MapControllerRoute(
    name: "default",
    pattern: $"{{controller=Home}}/{{action=Login}}/{{id?}}");

Here is the sample controller:

public class DaysOffController : Controller
{
    public IActionResult DaysOffSchedule(int? personnelId)
    {
        return View(nameof(DaysOffSchedule), new SingleStringModel($"Hello {personnelId}"));
    }

    [HttpPost]
    public IActionResult DaysOffSchedule(SingleStringModel model)
    {
        ModelState.Clear();
        model.Text = $"{model.Text}. OK";
        return View(nameof(DaysOffSchedule), model);
    }
}

Why is asp appending the last used parameter to a link, where I'm not providing any? The problem is obviously with the "days-off-schedule" route, but what am I doing wrong?


Solution

  • Why is asp appending the last used parameter to a link, where I'm not providing any? The problem is obviously with the "days-off-schedule" route, but what am I doing wrong?

    Based on your scenario and description, your days-off-schedule route defines a capture group for personnelId. This means it expects a value for that parameter whenever the route is used and when Url.Action is called without arguments for DaysOffSchedule, it tries to match the existing route.

    Since the route expects a value, ASP.NET MVC uses the "ambient value" - the personnelId from the previously visited page (Smith's schedule in your case). This value (2) gets included in the generated URL.

    So, you could try in following way:

    <a href='@Url.Action("DaysOffSchedule", "DaysOff", new { personnelId = (int?)null })'>My schedule</a>
    

    Refactor your Route Config:

    In addition, you should modify the route configuration as following:

      app.MapControllerRoute(
      name: "days-off-schedule",
      pattern: $"DaysOff/DaysOffSchedule/{personnelId?}", 
      defaults: new { controller = "DaysOff", action = "DaysOffSchedule" });
    

    Note: Keep in mind the "?" after personnelId. By making the personnelId parameter optional and specifying a default value of null, ASP.NET won't automatically fill in the parameter value when generating URLs unless you explicitly provide one. Please refer to this official document for more details and example.

    Is there a way to avoid adding a null param?

    Yes, you can avoid adding a null default value for the personnelId parameter by using two separate route definitions: one for the route with the parameter and another for the route without the parameter

    In that scenario, you should keep two route defination as following:

    app.MapControllerRoute(
        name: "days-off-schedule-with-id",
        pattern: "DaysOff/DaysOffSchedule/{personnelId}",
        defaults: new { controller = "DaysOff", action = "DaysOffSchedule" });
    
    
    app.MapControllerRoute(
        name: "days-off-schedule",
        pattern: "DaysOff/DaysOffSchedule",
        defaults: new { controller = "DaysOff", action = "DaysOffSchedule" });
    

    Alternative way:

    You can also use conditional rendering of your url action. You can do as following:

    @if (personnelId == null)
    {
        <a href='@Url.Action("DaysOffSchedule", "DaysOff")'>My schedule</a>
    }
    else
    {
        <a href='@Url.Action("DaysOffSchedule", "DaysOff", new { personnelId = personnelId })'>Employee Schedule</a>
    }