2

I'm using ASP.net MVC5 and I have the following:

[Route("Edit/{id}")]
[HttpGet]
public ActionResult Edit(int id)
{
    // do stuff
    return View(viewModel);
}

[Route("Edit")]
[HttpPost]
public ActionResult Edit(DraftViewModel draft)
{
    if (!ModelState.IsValid) return View(draft);
    // do stuff
    return RedirectToAction("Index");
}

I expected this to produce pretty URLs like this:

Draft/Edit/5

instead I get this:

Draft/Edit?id=5

Why is this? How can I get pretty URLs with attribute based routing? The code that generates the link with the ugly URLs is this:

@Html.ActionLink("Edit", "Edit", "Draft", new { id = draft.Id }, new { @class = "btn btn-primary btn-xs" })

UPDATE

When I remove the [POST] Action and only have [GET] (not useful of course), the URL on the GET looks pretty! So it is when I have two routes of the same name (but different verbs) that the framework trips up!

halfer
  • 19,824
  • 17
  • 99
  • 186
J86
  • 14,345
  • 47
  • 130
  • 228

2 Answers2

2

Make sure that attribute routing has been enabled in RouteConfig before convention-based routes

routes.MapMvcAttributeRoutes();

As you have already discovered when you have the POST action with different parameters to get the routing table uses a different format when generating the URLs as Asp.Net MVC framework is using a convention.

If you update the POST action's url template to match the format of the GET it will work as desired.

[Route("Edit/{id:int}")]
[HttpGet]
public ActionResult Edit(int id) {
    // do stuff
    return View(viewModel);
}

[Route("Edit/{id:int?}")]
[HttpPost]
public ActionResult Edit(DraftViewModel draft) {
    if (!ModelState.IsValid) return View(draft);
    // do stuff
    return RedirectToAction("Index");
}

Making the POST route template id parameter optional allows you to not have to update the action signature.

The resulting URL from ...

@Html.ActionLink("Edit", "Edit", "Draft", new { id = draft.Id }, new { @class = "btn btn-primary btn-xs" })

would be

Draft/Edit/5

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Thanks @Nkosi! Why do I need to pass the `id` again at the `[HttpPost] Edit`? – J86 Feb 09 '17 at 14:39
  • 1
    @Ciwan - Technically, you don't. When using Attributes, the order in which they are placed in the route table is undefined by default. You could either use Nkosi's solution to solve this, or you could place the `Order` attribute on each `[Route]` attribute to specify which order they go in the route table. See http://rion.io/2015/11/13/understanding-routing-precedence-in-asp-net-mvc/. However you solve it, specific routes need to be in the table first before general routes. – NightOwl888 Feb 09 '17 at 15:16
  • @Ciwan NightOwl is right. I totally overlooked the `Order` property of the `RoutAttribute`. – Nkosi Feb 09 '17 at 15:23
  • @NightOwl888's didn't work for me, see my comment on his answer – J86 Feb 10 '17 at 08:19
  • @Ciwan check updated answer. Updated route template to make id optional and not have to update action. still works. – Nkosi Feb 13 '17 at 14:48
0

Attribute routing works most of the time. But one major drawback to using attribute routing is that the order of attributes are undefined. Since in MVC routing, the order of the routes that are placed into the route table is critical for it to work right, you need to ensure that you use the Order property of the [Route] attribute when they are ambiguous.

In routing, the first match always wins, so it is absolutely critical to place specific routes before general routes in the route table.

[Route("Edit/{id}", Order = 1)]
[HttpGet]
public ActionResult Edit(int id)
{
    // do stuff
    return View(viewModel);
}

[Route("Edit", Order = 2)]
[HttpPost]
public ActionResult Edit(DraftViewModel draft)
{
    if (!ModelState.IsValid) return View(draft);
    // do stuff
    return RedirectToAction("Index");
}

Reference: http://rion.io/2015/11/13/understanding-routing-precedence-in-asp-net-mvc/

Community
  • 1
  • 1
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • I tried that, but it didn't work. The `[POST]` doesn't work. When I post the form, [I see this](https://www.screencast.com/t/5fOOF2EVt). However, the links do appear pretty, but without the post functionality, it is a no go! – J86 Feb 10 '17 at 08:18