I have read many posts about allowing different controller actions for different view-buttons. However, I cannot get this to work.
I use this code-snippet obtained from http://blog.ashmind.com/2010/03/15/multiple-submit-buttons-with-asp-net-mvc-final-solution/.
public class HttpParamActionAttribute : ActionNameSelectorAttribute
{
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
{
if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
return true;
if (!actionName.Equals("Action", StringComparison.InvariantCultureIgnoreCase))
return false;
var request = controllerContext.RequestContext.HttpContext.Request;
return request[methodInfo.Name] != null;
}
}
When I step through this code, I see a compare of actionName
with methodInfo.Name
. How can these EVER be equal when the whole purpose is to name the method different from the controller's action.
What is the return value of true or false actually mean as to the behavior/functionality?
Should I be overriding the 'fciContactUs' action with 'Action'?
The Controller = "HomeController"
[HttpParamAction]
[ActionName("Action")]
[AcceptVerbs(HttpVerbs.Post)]
[ValidateInput(false)]
public ActionResult DoClearForm(fciContactUs p_oRecord)
{
return RedirectToAction("fciContactUs");
}
[HttpParamAction]
[ActionName("Action")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult TrySubmit(fciContactUs p_oRecord)
{
// This must be the Submit command.
if (ModelState.IsValid)
{ ...etc....}
}
The View (view name is 'fciContactUs') form-start:
@using (Html.BeginForm("Action", "Home")) {
The view-buttons:
<input type="submit" name="TrySubmit" value="Submit" />
<input type="submit" name="DoClearForm" value="Clear Form" />
Further, this IsValidName
method ALWAYS returns false and the methods NEVER get executed.
I am concerned that there is an inconsistency in the action-name, the view-name, the controller-name, the button-names and the ActionNameSelectorAttribute
class override.
I am new to MVC and this whole thing has got me twisted up.
Any comments or assistance will be greatly appreciated.
Let's start with the
HttpParamActionAttribute
. This inherits fromActionNameSelectorAttribute
which:so, when applied to an action, and a request (get or post) comes in, this attribute will be called to see if that action is indeed the correct action.
It determines this by calling IsValidName (slightly confusing method, would be better 'IsMatchingAction' or similar). IsvalidName:
With parameters:
taken from the blog post, you can get the request and all the values on that request via the
controllerContext
.This returns
true
if it matches andfalse
if it isn't the action we're looking for.When this gets hit:
BeginForm
, hardcoded to "Action"TrySubmit
So the first check:
is just a catch-all in the case where you've specified something other than Action in your
BeginForm
so that it goes to the requested action (ie not "Action").The second check checks that you have specified "Action" in the
BeginForm
Now the "clever" part:
this checks all the
request
parameters to see if there is a parameter that matches the controller-action (method).When you click a
submit
button, thename
of that submit button is passed in the request parametersSo if you have:
if you click your "Submit", then:
and if you click your "Clear Form", then
the actual value ("Submit"/"Clear Form") isn't needed, only that it's not null.
So the code
checks the request parameters for the existance of an item which matches the current controller-action (method) name, eg:
To answer your first question:
The first compare is an override for when the
BeginForm
doesn't specify Action and theBeginForm
's action does match the controll-action (method) name.So, in your scenario, no they won't be equal and shouldn't be - it's the last check that's relevant.
Now the question is:
The problem is that your action names don't match the expected action names, because you have this:
so the
HttpParamAction
attribute finds your controller-action (method) and says "use this one", but then MVC says, but I'm looking for "Action" and that's not "Action" so I'll give you "The resource cannot be found". I'm not 100% of the reason it's doing this, but it's the same if you use MVC5Route("Action")
- it can't find the action having already matched it using the attribute.If you remove
[ActionName("Action")]
then all should be ok.Alternative: if you remove the first two checks (if form action = controller action and if form action != "Action") and only apply the attribute to the methods you need (and why would you apply it elsewhere?), then it seems to work fine.
Now, I'm using
[Route("")]
and changed:the form to:
the controller to:
(the initial
[HttpGet]
also has[Route("")]
)and the attribute to:
As an alternative, it looks like your "ClearForm" button simply redirects to the page. You could do this more easily with a simple
@Html.ActionLink("Clear Form", "fciContractUS")
and a bit of css to make it look like a button.You'll also have an issue if you have any client-side validation (eg required fields) as you won't be able to "submit" to clear the form until they have values.