Neil Barnwell
Neil Barnwell

Reputation: 42125

ASP.NET MVC ActionLink: Strange URL generated

Why does the following Html.ActionLink call:

Html.ActionLink("Approve", "Advance", new { id = Model.ID, step = StepType.Approve })

generate a URL with query parameters rather than a "restful" URL, i.e.:

http://localhost/Website/Case/Advance/1?step=Refer

I only have the default route registered, do I need additional routes that can understand what the "StepType" parameter is?

I've tried adding this route in after the default route:

routes.MapRoute(
    "CaseAdvance",
    "{controller}/{action}/{id}/{step}",
    new {controller = "Case", action = "Advance", id = "", step = StepType.Refer});

but it had no effect. Adding the new route registration before the default gave me an error:

The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int64' for method 'System.Web.Mvc.ActionResult Advance(Int64, Website.Core.StepType)' in 'Website.Controllers.CaseController'. To make a parameter optional its type should be either a reference type or a Nullable type.

Upvotes: 2

Views: 2547

Answers (4)

Robert Koritnik
Robert Koritnik

Reputation: 105029

Custom route catching too much at the moment

Your exception only tells you that your custom route is catching something that you didn't intend it to catch. So if there would be a request to your application root URL:

http://localhost/Website

your custom route would catch it. And set it's defaults. And call your CaseController.Advance() action. And of course throw an exception, because id is not defined.

Keep your custom route in front of default one

But you will have to change your custom route or add route constraints to it so it will actually catch only those requests that it's meant to catch.

But which change should you do? If there's only going to be a single controller that needs it than change it to:

routes.MapRoute(
    "CaseAdvance",
    "Case/{action}/{id}/{step}",
    new { controller = "Case", action = "Advance", id = "", step = StepType.Refer});

If there are other controllers a well, you can keep it as it was, just add constraints:

routes.MapRoute(
    "CaseAdvance",
    "{controller}/{action}/{id}/{step}",
    new { controller = "Case", action = "Advance", id = "", step = StepType.Refer},
    new { controller = "Case|Other" });

If there can be any controller, you can make a requirement for your id to be numeric:

routes.MapRoute(
    "CaseAdvance",
    "{controller}/{action}/{id}/{step}",
    new { controller = "Case", action = "Advance", id = "", step = StepType.Refer},
    new { id = @"\d+" })

In this case this route will only catch those requests that actually have an id defined. As a number of course.

You will know which one suits you best.

Upvotes: 2

Mark Coleman
Mark Coleman

Reputation: 40863

Try using RouteLink and see if that works for you.

Html.RouteLink("Approve", "CaseAdvance", new { controller = "Case", action = "Advance", id = Model.ID, step = StepType.Approve })

If calling RouteLink produces a valid link it will at least mean your route is setup correctly.

Upvotes: 1

Simon Holman
Simon Holman

Reputation: 150

In your route you have specified a parameter of stepType but your passing a parameter called step.

Your actionlink parameters names must match the route parameter names or you will get exactly what your seeing.

EDIT: Ok, you changed your code while I was typing this answer!!

Upvotes: 1

Blair Scott
Blair Scott

Reputation: 1885

Yes, if there is no route such as "{controller}/{action}/{id}/{step}" then the ActionLink method will simply pass "step" as a querystring parameter.

Upvotes: 2

Related Questions