Search code examples
c#asp.net-mvcasp.net-mvc-5asp.net-mvc-routingattributerouting

AttributeRouting to Fix Two Params named Id


So I've got a project using the default Route of

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

I just ran into a situation described 7 years ago at MVC Pro Tip: Don't use the "id" URL parameter in your routes.

The solution they have is great and all, but at this moment, I do not want to change my entire site. I was hoping to fix my issue with Attribute Routing.

However, I cannot seem to get this to work, and I receive a 404 Error page. (just in case the link above does not work, I'll describe the code in detail here).

Details

In my project I use ViewModels. The ViewModel is (very simply) defined as:

public class Foo {
    public int Id { get; set; }
    ...
}

My BarController is as follows:

public ActionResult Create(string id) {
    if (string.IsNullOrWhiteSpace(id)) {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    ...
}

[HttpPost]
public ActionResult Create(string id, Foo viewModel) {
    if (string.IsNullOrWhiteSpace(id)) {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    ...
}

Error

When I navigate to /Bar/Create/abc123, I see my form just fine. However, when I submit the form, Model.IsValid is false. Looking at Watch window for the this.ModelState object, I found the error message to say

The value 'abc123' is not valid for Id.'

I assume that's because the model binder is trying to bind abc123 to Id on the ViewModel which has an int as the Id property.

What I've Tried

Here's what I've attempted to do so far on my Controller:

[Route("Bar/Create/{aid}", Name = "FooBarRouteName")]
public ActionResult Create(string aid) {
    if (string.IsNullOrWhiteSpace(aid)) {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    ...
}

[HttpPost]
public ActionResult Create(string aid, Foo viewModel) {
    if (string.IsNullOrWhiteSpace(aid)) {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    ...
}

The problem now is when I navigate to /Bar/Create/abc123, I get a 404 Error page and cannot even try to submit the form.

Can someone point me in the right direction or figure out what I'm doing wrong? Thanks!


Solution

  • First ensure that attribute routing is enabled before convention-based routes to avoid route conflicts.

    //Attribute routing
    routes.MapMvcAttributeRoutes();
    
    //Convention-based routing
    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
    

    You are missing a route on the POST action.

    If using attribute routing you have to decorate all actions on the controller

    [HttpGet]
    [Route("Bar/Create/{aid}", Name = "FooBarRouteName")] // GET Bar/Create/abc123
    public ActionResult Create(string aid) {
        if (string.IsNullOrWhiteSpace(aid)) {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        ...
    }
    
    [HttpPost]
    [Route("Bar/Create/{aid}")] // POST Bar/Create/abc123
    public ActionResult Create(string aid, Foo viewModel) {
        if (string.IsNullOrWhiteSpace(aid)) {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        ...
    }