Grizzly
Grizzly

Reputation: 5943

Unable to Submit Form when using RouteAttribute, Submitting to wrong Action

Here are my controller actions:

HttpGet

// GET: ControllerName/Create
[Route("CreateDocument/{personId}")]
public ActionResult Create(int personId)
{
    var personDocumentation = new PersonDocumentation()
    {
        PersonId = pilotId
    };
    ViewBag.DocumentationTypeIdSelection = new SelectList(db.DocumentationTypes, "Id", "DocumentationTypeName");

    return View(personDocumentation);
}

HttpPost

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,PersonId,DateReceived,DocumentationTypeId,Filepath")] PersonDocumentation personDocumentation)
{
    if (ModelState.IsValid)
    {
        if (Request.Files.Count > 0)
        {
            // performing stuff here
        }

        db.PersonDocumentations.Add(personDocumentation);
        db.SaveChanges();
        return RedirectToAction("Index", "PersonDocumentations", new {pilotId = personDocumentation.PilotId});
    }

    ViewBag.DocumentationTypeId = new SelectList(db.DocumentationTypes, "Id", "DocumentationTypeName", personDocumentation.DocumentationTypeId);
    return View(personDocumentation);
}

View/Form

@using (Html.BeginForm("Create", "PersonDocumentations", FormMethod.Post, new {enctype = "multipart/form-data" }))
{
    // Form stuff here

    <div class="form-group">
        <input type="submit" value="Create" class="btn btn-lg btn-success" />
    </div>
}

When debugging, on submit I'm being redirected to the HttpGet Create action with the route attribute. Can I submit to a Post Action when the corresponding Get action has a route attribute?

UPDATE

After looking at Nkosi answer down below.. I have this:

[HttpGet]
[Route("CreateDocument/{personId}")]
public ActionResult Create(int personId)
{
    // stuff here
}

[HttpPost]
[Route("CreateDocument")]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,PersonId,DateReceived,DocumentationTypeId,Filepath")] PersonDocumentation personDocumentation)
{
    // stuff here
}

On my view page.. to get the HttpGet action I have a button link:

@Html.ActionLink("Create New Documentation", "Create", new {personId = Model.PersonId}, new {@class = "btn btn-info"})

But, when I hover over the button I see the link on the lower left hand corner:

http://localhost:xxxxx/CreateDocument?personId=4

Shouldn't that be:

http://localhost:xxxxx/CreateDocument/4

When I remove the Route attribute from the HttpPost action, the url at the lower left corner shows as http://localhost:xxxxx/CreateDocument/4

Then when I click on the button I receive 404 errors:

Requested Url:  /CreateDocument


public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapMvcAttributeRoutes();

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

Upvotes: 4

Views: 443

Answers (1)

Nkosi
Nkosi

Reputation: 247163

You are mixing attribute and convention-based routing.

If using attribute routing on the controller you need to go all in. When using multiple actions you also have to include [Http{Verb}] to further distinguish the action routes.

public class PersonDocumentationsController : Controller {

    [HttpGet]
    public ActionResult Index() {
        //...
    }

    //GET CreateDocument/4
    [HttpGet]
    [Route("CreateDocument/{personId:int}")]
    public ActionResult Create(int personId) {
        //...
    }

    //POST CreateDocument/4
    [HttpPost]
    [Route("CreateDocument/{personId:int}")]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "Id,PersonId,DateReceived,DocumentationTypeId,Filepath")] PersonDocumentation personDocumentation) {
        //...
    }
}

This also assumes that there is no RoutePrefix applied to the controller.

So now when you call

@Html.BeginForm("Create", "PersonDocumentations", FormMethod.Post, new {enctype = "multipart/form-data" }))

in the PersonDocumentationsController.Create view it will map to the correct action.

POST CreateDocument/4

For action links you need to also include the desired controller

@Html.ActionLink("Create New Documentation", "Create", "PersonDocumentations" , new {personId = Model.PersonId}, new {@class = "btn btn-info"})

Which should map to

GET http://localhost:xxxxx/CreateDocument/4

Reference Attribute Routing in ASP.NET MVC 5

Upvotes: 4

Related Questions