In my layout I'm using RenderAction that will output a simple email form which will post to an action.
// In _Layout.cshtml
#{Html.RenderAction("Email", "Home")};
// Home Controller
[ChildActionOnly]
public ActionResult Email(){}
[HttpPost]
public ActionResult Email(params){}
Now the RenderAction works great when it's the only form on the page. If it's not, however, the Email Post action gets called in addition to the other form action which I don't understand. The forms are not nested; the source looks something like this:
<form id="email-form" action="/home/email" method="post">
// form elements
</form>
<form id="some-other-form" action="/somecontroller/someaction" method="post">
// form elements
</form>
How can I avoid the post of the child action when it comes from a different form? As a workaround I can check the paramaters passed in and if they're null I should be fine and dandy, but I don't think this is the intended behavior.
this.ControllerContext.IsChildAction; in your controller will give you the ability to check if the post is coming from the URL or through a Html.RenderAction.
That might help you debug it a little better.
Related
I have this contact form which I want to use on two pages (two views) in MVC 5.x (razor viewengine) So I have put the form in an partialview called _Contact and I have read that RenderAction is the best approach if you do not have the required data for the partialview in your model and if it is more standalone (seperate from the rest of the view)
So I call it like this:
#{ Html.RenderAction("SendMail", "Uk");}
My Uk controller has these two methods:
[HttpGet]
public PartialViewResult SendMail()
{
return PartialView("_Contact");
}
[HttpPost]
public PartialViewResult SendMail(FormCollection fc)
{
// send mail using values out of the form (sorry did not feel like building a complete model for it
ViewBag.Succeed = true;
// if smtpclient could not reach server etc. it returns false
return PartialView("_Contact");
}
it all works, but the PartialView is only rendered, not on the placeholder where i call the RenderAction. It works all great, but after the post it just displays the partial view and not the "parent view" and the shared layout view etc. I hope that I made myself clear. Please let me know if I need to add more info.
This is the BeginForm from my shared view:
using (Html.BeginForm("SendMail", "Uk", FormMethod.Post))
It will not work as expected for your current code, because when you post the form, it returns Partial View not complete View. If you want to get only partial view then you have to submit your form via Ajax.
In ajax's success handler you will get HTML of your partial view and that you can put in a DIV tag of partial view container.
This link will give you a better idea about Posting Partial View via Ajax.
ASP.NET MVC Partial view ajax post?
I know that maybe the title sounds a bit weird but I believe that my problem is weird indeed. I have an ASP.NET MVC 4 application (this is my first MVC real-world application) with Razor view-engine.
I have a layout view where I'm rendering two partial views like this:
<!-- Login -->
#Html.Action("RenderLoginPopup", "Login")
<!-- Registration -->
#Html.Action("RenderRegisterPopup", "Login")
Each of those actions from the Login controller just renders a partial view:
[ChildActionOnly]
public ActionResult RenderLoginPopup()
{
return PartialView("Partial/_LoginPopupPartial");
}
Just for exemplification sake (both are built the same way), the login partial view contains an ajax form like this:
#using (Ajax.BeginForm("Login", "Login", new AjaxOptions()
{
HttpMethod = "POST",
OnSuccess = "loginResponseReceived"
}, new { #id = "loginForm" }))
The Login action from the Login controller (the target of the form) is signed with the following attributes (worth to mention and notice the HttpPost one):
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public JsonResult Login(LoginModel model)
{ }
So far, so good... Everything works perfect - both the login and the register actions are working without any issues.
The issue that I want to speak about shows-up when I have a #Html.BeginForm() in a view that is loaded along with the main layout. For example, if I have a pure and simple form like this:
#using (Html.BeginForm())
{
<input type="hidden" name="name"/>
<input type="submit" value="Send"/>
}
along with the controller CaptionExtendedController:
[HttpPost]
public ActionResult Index(string nume)
{
return View();
}
So, in the end, in my final html generated file I will have 3 forms - 2 for login and register (ajax) and one simple form generated from the last view. Please keep in mind that all three forms are independent (meaning that they are not one in another).
The issue is that everytime I'm pressing the button "Send" from the last form all controllers that are signed with the [HttpPost] attribute from my view (Login, Register from LoginController and Index from CaptionExtendedController) gets called.
WHY??? In order to have a temporary fix, I've removed the [HttpPost] attribute from the Login and Register actions and now it's working but I don't think this is correct.
Please, there is someone who can explain me why this is happening and eventually point me to the right direction in fixing this issue?
Thank you in advance.
Try specifying the controller and action with your Html.BeginForm and we can start from there. Also, you can utilize #Html.RenderPartial to render your partials which would get rid of some of your unneeded actions/controllers, making it a bit more manageable.
This doesn't address the root problem, but might be a work-around which is all you need anyway. :)
You could write some jQuery which catches the button click and then submits the form directly by name. For example, if you add the id below:
#using (Html.BeginForm())
{
<input type="hidden" name="name"/>
<input type="submit" id="regularSubmitButton" value="Send"/>
}
Then you could write:
$(document).ready(function() {
$("#regularSubmitButton").click(function(e) {
e.preventDefault();
$(this).parent("form").submit();
return false;
});
});
I'm not sure it would work without seeing everything, but seems to be worth a try.
Cheers,
Michael
It doesn't make sense anymore... Starting from the comments and answers, I've mapped 3 functions to the submits on those tricky forms: login, register and index (also I've put back the HttpPost attribute to the Login and Register actions). In those jquery functions I've just put an alert with a string (name of the form); in order to be able to write a jquery id-based selector, I've declared also the last form with an id (it didn't had it; only login and register had one), like this (an example taken from another form with the same issue):
#using (Html.BeginForm("Index", "PersonalizeCard", new {data = Model.EncryptedDataQueryStringValue}, FormMethod.Post, new {#id = "personalizeCardForm"})) { }
(what has been added - last two parameters - form method and html id).
After this, I've run the application and no exception anymore... I've put breakpoints on the login and register actions - nothing... I've even removed those 2 extra parameters from the BeginForm - still nothing...
WHY???? Again, why??? I mean, I'm not upset that it's fixed but I don't understand why it's fixed by itself...
THANK YOU FOR ALL YOUR TIME AND COMMENTS / ANSWERS.
So we have [HttpPost], which is an optional attribute. I understand this restricts the call so it can only be made by an HTTP POST request. My question is why would I want to do this?
Imagine the following:
[HttpGet]
public ActionResult Edit(int id) { ... }
[HttpPost]
public ActionResult Edit(MyEditViewModel myEditViewModel) { ... }
This wouldn't be possible unless the ActionMethodSelectorAttributes HttpGet and HttpPost where used.
This makes it really simple to create an edit view. All the action links just points right back to the controller. If the view model validates false, you just pop right back to the edit view again.
I will be bold and say this is best practice when it comes to CRUDish things in ASP.NET MVC.
EDIT:
#TheLight asked what was needed in the view to accomplish the post. It's simply just a form with method POST.
Using Razor, this would look something like this.
#using (Html.BeginForm())
{
<input type="text" placeholder="Enter email" name="email" />
<input type="submit" value="Sign Up" />
}
This renders the following HTML:
<form action="/MyController/Edit" method="post">
<input type="text" name="email" placeholder="Enter email">
<input type="submit" value="Sign Up">
</form>
When the form is submitted, it will perform an Http Post request to the controller. The action with the HttpPost attribute will handle the request.
Its so you can have multiple Actions that use the same name, you can use the HttpPost attribute to mark which method gets handled on a Post request like so:
public ActionResult ContactUs()
{
return View();
}
[HttpPost]
public ActionResult ContactUs(ContactUsModel model)
{
//do something with model
return View();
}
As far as best practices for HttpGet and HttpPost, it is good practice in any web development to use HttpPost for Creates, Updates, and Deletes (data modification). Post is good because they require a form submission, which prevents users from clicking poisoned links(e.g. [https://www.example.com/Delete/1]) in emails, social sites, etc. and changing data inadvertently. If you are basically just Reading data HttpGet works great.
See OWASP for more in-depth security considerations and why the validation token increases security.
This is mainly so that you can have two Actions with the same name,
one which is used on GETs and perhaps displays a form for user entry and the other being used on POSTs when the user submits the form displayed by the original GET. If the Actions are not differentiated in this way, an error will occur due to being unable to resolve which Action is intended to handle the request.
In mvc the page is not get post back as in asp.net then how can we perform postback operations in asp.net mvc2. for ex how to perform particular action when someone selects a chech box?
Thanks in Advance
The mechanism behind the postback model in WebForms is called HTTP POST. This is how user input is communicated back to the server.
You can do it manually. Attach a JavaScript handler manually to the checkbox "onclick" event and perform a POST request to some url. There, this request will hit some controller action where you do what you want. For example, update the model (check/uncheck the checkbox) and return the same view from which the POST originated. The view will now show the different state for the checkbox.
The WebForms mechanisms do pretty much the same, though these things are abstracted away from you. With ASP.NET MVC you'll need to learn how to do it on your own (which is always a good thing).
Your MVC Action method on your Controller somewhat is your 'PostBack' handler.
Start with a simpler example; a simple HTML form post:
<form action="/MyController/MyAction" method="post">
<input type="text" name="myName" />
<input type="submit />
</form>
Now in your controllers action you can get the posted values and perform your tasks. When done give the browser back what it needs:
public class MyController: Controller
{
public ActionResult MyAction(string myName)
{
// Do something with myName
return new ContentResult { Content = "Hello " + myName };
}
}
As for a checkbox, it is different. You need to learn Javascript (jQuery is the most used library to use with that) and post the action using that. For example you can wire up to the check box 'onclick()' event and perform an XHR - a browser specific Javascript operation, post (you can use jQuery for that too) to your controller.
So you need to start thinking differently than webforms abstractions and get involved with HTML, HTTP and Javascript.
you can Put this inside an MVC Razor page:
if (Request.HttpMethod=="POST") {
}
I'm currently in the process of learning ASP MVC and am running into a few issues.
First, when I use
<%=Http.ActionLink("Add / Modify", "AddModify" %>
it will show as Add / Modify (/Home/AddModify) in Firefox and Add / Modify in IE. It is doing that to all links in FF and none in IE. Anyone know what reasoning is for that?
Edit: What is displayed in the browser (FF in this case) is "Add / Modify (/Home/AddModify)" while in IE shows just "Add / Modify".
Here is a screenshot of what I see on my site in FireFox: http://img6.imageshack.us/img6/1331/19748435.png
You can see how it shows the text and the appropriate link afterwards (only populated in Database with /).
Also, I am trying to have buttons (both standard and image) on my site which will link to new pages, while also performing hidden tasks (saving data, etc...). Anyways, When I do the following:
<form method="post" action="/Home">
<input type="submit" value="AddModify">
</form>
and the controller has a simple
[ActionVerbs(HttpVerbs.Post)]
public ActionResult AddModify()
{
return View();
}
And I still cannot get that function to call, yet when I do http://localhost:port/Home/AddModify, the function calls and I can get to that page. I am doing it this way because there may be code that has to execute before redirecting to that page, rather than just a direct link to that page. I have tried with and without the ActionVerbs line, I have tried this form of the Html Form:
<% using (Html.BeginForm()) { %> ... <%}%>
and still nothing. I have also tried no form and still nothing, but here's something that may affect this...I am using a master page with everything inside a content place holder inside a form that uses runat="server". Would that matter? So its Master Page -> form (masterform runat server) -> ContentPlaceHolder -> form (for postback and action) -> submit button. Any help would be greatly appreciated.
If I am thinking correctly, your form action should be calling the name of the action method:
<form method="post" action="/Home/AddModify">
<input type="submit" value="AddModify">
</form>
The ActionLink would be the same way.
Otherwise you will need to modify your routes to go to that action method by default.
Let's do this in two parts
1 I think your ActionLink should be:
<%=Http.ActionLink("Add / Modify", "AddModify", "Home")
...to force routes.
First parameter: text shown
Second parameter: action name
Third parameter: controller name
2 Change your submit button to: (I assume we're currently looking at your "index" action from your "Home" controller)
<form method="post" action="/Home">
<input type="submit" value="AddModify" name="ModifyBtn" >
</form>
Then in your Home controller:
edit:
//GET
public ActionResult Index()
{
return View();
}
/edit
[ActionVerbs(HttpVerbs.Post)]
public ActionResult Index(FormCollection form, string ModifyBtn, string OtherBtn)
{
if (ModifyBtn!=null)
{
//do stuff
return RedirectToAction("AddModify");
}
if (OtherBtn!=null)
{
//do stuff
return RedirectToAction("OtherAction");
}
return View();
}
edit:
I think you're trying to submit directly to another Action. The best way is to handle the POST method inside your code then redirect to another action. That way, you can use
<% using (Html.BeginForm()) { %>
without trouble.
<form method="post" action="/Home">
Will create a form with a href of /Home, this will only call the AddModify action if the AddModify action is default on that route.