Robert
Robert

Reputation: 2533

Url.Action generates query instead of parameter URL

This is the controller class. I am showing only method signatures.

[Authorize]
[RoutePrefix("specification")]
[Route("{action=index}")]
public class SpecificationController : BaseController
{
    [HttpGet]
    [Route("~/specifications/{subcategoryID?}")]
    public ActionResult Index(int? subcategoryID);

    [HttpPost]
    [Route("get/{subcategoryID?}")]
    public JsonResult Get(int? subcategoryID);

    [HttpGet]
    [Route("~/specifications/reorder/{subcategoryID}")]
    public ActionResult Reorder(int subcategoryID);

    [HttpGet]
    [Route("new/{id?}")]
    public ActionResult New(int? id);

    [HttpGet]
    [Route("edit/{id?}")]
    public ActionResult Edit(int id);

    [HttpPost]
    [ValidateAntiForgeryToken]
    [Route("edit")]
    public JsonResult Edit(SpecificationJson specification);

    [HttpPost]
    [Route("moveup")]
    public JsonResult MoveUp(int specificationID);

    [HttpPost]
    [Route("movedown")]
    public JsonResult MoveDown(int specificationID);

    [HttpDelete]
    [Route]
    public ActionResult Delete(int id);
}

The problem is that calling

@Url.Action("index", "specifications", new RouteValueDictionary() { { "subcategoryID", @subcategory.SubcategoryID } })

returns

/specifications?subcategoryID=15

instead of

/specifications/15

Why is this happening? I do not have any similar methods on that route expect this one!

Upvotes: 6

Views: 3130

Answers (2)

NightOwl888
NightOwl888

Reputation: 56869

Your call to generate the URL is incorrect. To match the controller name, it should be "specification" not "specifications".

@Url.Action("index", "specification", new { subcategoryID=subcategory.SubcategoryID })

Keep in mind, the URL specified in the [Route] attribute is only cosmetic. Your route values must match the controller name and action method name for it to utilize that route to generate the URL.

To make this more clear for those maintaining the code (and slightly faster), it might be better to make the parameter values Pascal case just like the controller and action names.

@Url.Action("Index", "Specification", new { subcategoryID=subcategory.SubcategoryID })

Why is this happening?

-------------------------------------------------------------
| Route Key          | Route Value   | Your Action Request  |
|--------------------|---------------|----------------------|
| Controller         | Specification | Specifications       | No Match
| Action             | Index         | Index                | Match
| subcategoryID      | ?             | XXX                  | Match (Always)
-------------------------------------------------------------

To get a route match, all parameters of @Url.Action must match the route value dictionary. The problem is that Controller=Specifications is not defined in the route value dictionary because your actual controller's name is SpecificationController. Therefore, the route value name is Specification regardless of what you put in the [Route] attribute. The URL ~/specifications/{subcategoryID?} has nothing at all to do with an outgoing (URL generation) match - it only matches incoming URLs and determines what the URL will look like when it is generated.

If you want to use Specifications instead of Specification for the route value, you need to move the action method to a new controller named SpecificationsController. That said, I don't see what difference it makes, since the end user won't see the route value name anyway.

Upvotes: 3

Mihai Alexandru-Ionut
Mihai Alexandru-Ionut

Reputation: 48407

You have to use this in order to generate follow url: /specifications/15

@Url.Action("index", "specifications", new { subcategoryID=subcategory.SubcategoryID })

[Route("~/specifications/{subcategoryID?}")]
public ActionResult Index(int? subcategoryID);

What did I do wrong and how can I revert to using subcategoryID as parameter name ?

You have to add another route (before DEFAULT ROUTE) in order to have another optional parameter:

Something like this:

 routes.MapRoute(
         "SecondRoute",
         "{controller}/{action}/{subcategoryID}",
          defaults: new { controller = "Home", action = "Index", subcategoryID = UrlParameter.Optional }
 );

Upvotes: 0

Related Questions