Wednesday 16 December 2009

ASP.NET MVC Supporting Multiple Actions Through a Single Form

One of the things that always plagues me in ASP.NET MVC is supporting multiple actions through a single form.  Sometimes this is not a good idea, but often we need to do it to support multiple functions on the same data.  I’ve tried using javascript to override the form action but it takes a lot of complicated code and is much harder when using ajax calls. I’ve tried calling a single action and then splitting out the data to call the sub methods but unless you use redirections it makes your methods much harder to test and you lose your form if you redirect, plus the code is messy.

Finally I can across this post by MAARTEN BALLIAUW.  It specifies that you can use a custom attribute to determine which action is valid depending on other parameters.  It’s very smart and I highly suggest you read it, in fact I’m going to assume at this point that you have.

I took it a little bit further and have some hints for you.  You’ll likely get an error first time you try it saying that there is ambiguity between the actions, be sure that your form is calling an action that does not exist.  This code is going to decide if an action is valid, if the Index action is valid and the form post variable also says your post action is valid then it’s going to throw this exception.  An action that doesn’t exist can’t be valid, so only the one related to your form post variable will be valid.

Also, I don’t like to use the value of the submit button.  The value is also what the user sees and is likely to be changed by the business.  For this reason all I want to do is verify that the form variable was submitted.

I’ve changed the code in the attribute to read:

   1: public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
   2: {
   3:    //return controllerContext.HttpContext.Request[MatchFormKey] != null &&
   4:    //    controllerContext.HttpContext.Request[MatchFormKey] == MatchFormValue;
   5:    // Commented out because we don't want to match the value, just the key.
   6:  
   7:    if (MatchFormValue == null)
   8:    {
   9:        return controllerContext.HttpContext.Request[MatchFormKey] != null;
  10:    }
  11:    else
  12:    {
  13:        return controllerContext.HttpContext.Request[MatchFormKey] != null &&
  14:            controllerContext.HttpContext.Request[MatchFormKey] == MatchFormValue;
  15:    }
  16: }

It’s a fantastic solution so far, my thanks to Maarten.

No comments: