ASP.NET MVC Controller actions with the same name - c#

I've got a controller in MVC5 for an apply form. There is a GET to return the view and a POST to take the form data from the application and process it:-
public ActionResult Apply(string jobReference)
{
Apply form = new Apply();
Session[Settings.JOBREFERENCE] = jobReference;
return View(form);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Apply(Apply form)
{
if (ModelState.IsValid)
{
}
}
When I press the to submit the form, it calls the GET controller instead of the the POST controller, despite it having [HttpPost] on the latter.
If I actively renamed the HTTPPost controller and navigate to it, I see a Server Error, with a message saying it cannot be found. Submitting the form causes the page to crash.
Form Code
#model Salt.Models.Apply
<div class='applyForRoleExt popupBox'>
<img src="/img/closePopup.png" alt="close" />
<div class="innerContainer">
#using (Html.BeginForm("Apply", "Form", FormMethod.Post, new { enctype = "multipart/form-data", #id = "ApplyForm" }))
{
#Html.AntiForgeryToken()
<h6>Apply for this role</h6>
#*<div class="haveAccount">
<span>Have an account? Apply quickly by logging in now</span>
Login to my account
</div>*#
<div class="leftCol">
<label>Name<span>*</span></label>
<div class="inputBox">
#Html.TextBoxFor(m => m.Name)
</div>
<label>Email Address<span>*</span></label>
<div class="inputBox">
#Html.TextBoxFor(m => m.EmailAddress)
</div>
<label>Telephone Number<span>*</span></label>
<div class="inputBox">
#Html.TextBoxFor(m => m.TelephoneNumber)
</div>
<label>Portfolio Link<span>*</span></label>
<div class="inputBox">
#Html.TextBoxFor(m => m.PortfolioLink)
</div>
</div>
<div class="rightCol">
<label>CV Upload</label>
<div class="inputBox">
#Html.TextBoxFor(m => m.CV, new { #type = "file" })
</div>
<label>Covering Note</label>
<div class="inputArea">
#Html.TextAreaFor(m => m.CoveringNote)
</div>
</div>
<div class="actions">
<p class="terms">By submitting this form you consent to Salt Recruitment processing your personal data in accordance with our privacy policy and agree to future contact material.</p>
<!--<button class="submit">Apply Now</button>-->
<input type="submit" name="submit" value="Apply Now" />
</div>
}
</div>
</div>
<script type="text/javascript">
$(document).ready(function () {
$.validator.unobtrusive.parse("#ApplyForm");
});
</script>
Thanks,
Mike.

It's been fixed. It took a second pair of eyes but a rule in the web.config was removing the trailing slash from the form post URL and it was defaulting to loading the GET. No idea why that would cause the problem but it did.

Related

c# mvc retrieve data from a view

I want to retrieve data from a view, it should work like this:
User fill a form available on the webpage
User clicks SEARCH button
Some function(s) collect the data and display them in another view
I tried all the basic tutorials and tips on others stackoverflow question but it still doesn't work. I don't know what I'm doing wrong...
Here's my code from the view:
section id="roomSearch">
<div class="banner">
<div class="banner-info">
<div class="container">
<div class="details-1">
#using (Html.BeginForm("UploadRoomSearchData", "HomeController", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="col-md-10 dropdown-buttons">
<div class="col-md-3 dropdown-button">
#Html.AntiForgeryToken()
<div class="input-group">
#Html.TextBoxFor(m => m.YourName, new { #class = "form-control has-dark-background", #placeholder = "Imię" })
#Html.ValidationMessageFor(m => m.YourName, "", new { #class = "text-danger" })
<!--<input class="form-control has-dark-background"
name="slider-name" id="slider-name" placeholder="Imię" type="text" required="">-->
</div>
</div>
<!---strat-date-piker---->
<link rel="stylesheet" href="~/Content/jquery-ui.css" />
<script src="~/Scripts/jquery-ui.js"></script>
<script>
$(function () {
$("#datepicker,#datepicker1").datepicker();
});
</script>
<!---/End-date-piker---->
<div class="col-md-3 dropdown-button">
<div class="book_date">
<form>
<input class="date" id="datepicker" type="text" value="Przyjazd" onfocus="this.value = '';" onblur="if (this.value == '') {this.value = 'Przyjazd';}">
<!-- #Html.TextBoxFor(m => m.CheckIn, new { #class = "date" })
#Html.ValidationMessageFor(m => m.CheckIn, "", new { #class = "datefield" })-->
</form>
</div>
</div>
<div class="col-md-3 dropdown-button">
<div class="book_date">
<form>
<input class="date1" id="datepicker1" type="text" value="Wyjazd" onfocus="this.value = '';" onblur="if (this.value == '') {this.value = 'Wyjazd';}">
<!--#Html.TextBoxFor(m => m.CheckOut, new { #class = "date1" })
#Html.ValidationMessageFor(m => m.CheckOut, "", new { #class = "datefield" })-->
</form>
</div>
</div>
<div class="col-md-3 dropdown-button">
<div class="section_1">
<select id="country" onchange="change_country(this.value)" class="frm-field required">
<option value="null">Dwuosobowy</option>
<option value="null">Jednoosobowy</option>
<option value="AX">Apartament</option>
<option value="AX">Gościnny</option>
</select>
</div>
</div>
<div class="clearfix"> </div>
</div>
<div class="col-md-2 submit_button">
<form >
<input type="submit" value="SZUKAJ">
<!-- <p> #Html.ActionLink("SZUKAJ", "Book1", "Home")</p>-->
</form>
</div>}
And here's my code in the controller. For now I try to retrieve only a name, to see if it's working.
[HttpPost]
public ActionResult UploadRoomSearchData(FormCollection form)
{
string name = Request["YourName"].ToString();
StringBuilder sbRoom = new StringBuilder();
sbRoom.Append("<b>Amount :</b> " + name + "<br/>");
//return RedirectToAction("Book1");
return Content(sbRoom.ToString());
}
I also tried something like this:
foreach(var v in form)
{
Write.Response("name:" + v);
}
I tried your code and it seems to work.
First I have the controller method to display the form
public ActionResult CreatePerson()
{
Person model = new Person();
return View(model);
}
Then the form:
#model RetrieveDataFromaView.Models.Person
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Person</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.YourName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.YourName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.YourName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="submit" class="btn btn-default" />
</div>
</div>
</div>
}
Which does a post to the controller method
[HttpPost]
public ActionResult CreatePerson(FormCollection formCollection)
{
string name = Request["YourName"].ToString();
StringBuilder sbRoom = new StringBuilder();
sbRoom.Append("<b>Amount :</b> " + name + "<br/>");
return Content(sbRoom.ToString());
}
This returns a view with only the content of the StringBuilder.
Maybe you are looking for RedirectToAction?
Hello you have this line inside the form:
#Html.AntiForgeryToken()
You can remove it or add the corresponding attribute to use it:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreatePerson(FormCollection formCollection)
{
///Your code here
}
Basically this is a token generated for the server to avoid requests from forms not generated by the server.
You have many ways of retrieving data from a form Post in ASP.NET MVC.
Using a Model
Usually, forms are created by specifying a Model type in the Razor view. You can use that type to retrieve the data. ASP.NET MVC will parse the body and populate the object in parameter for you.
Ex:
Controller:
public class HomeController: Controller
{
[HttpGet]
public ActionResult Index()
{
return View(new Person());
}
[HttpPost]
public ActionResult Index(Person p)
{
//Just for the sake of this example.
return Json(p);
}
}
Razor view
#model WebApplication2.Models.Person
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>title</title>
</head>
<body>
<div>
#using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div>
#Html.LabelFor(m => m.FirstName): <br/>
#Html.TextBoxFor(m => m.FirstName)
</div>
<div>
#Html.LabelFor(m => m.LastName): <br/>
#Html.TextBoxFor(m => m.LastName)
</div>
<input type="submit" value="Submit" />
}
</div>
</body>
</html>
Using a FormsCollection
The FormsCollection object allows you to access the raw values of a form. It acts as a Dictionary for the Forms value. This is useful, especially when you have a dynamic model to parse, or if you just plain don't know about the Model type.
It's also pretty straightforward to use.
[HttpPost]
public ActionResult Index(FormCollection form)
{
var dict = form.AllKeys.ToDictionary(key => key, key => form[key]);
return Json(dict);
}
PS: I saw you are using Request[key]. It may just be me, but this call just looks like Dark magic, where you get data from who knows where (it uses the Query String, the cookies, the Request body, etc. It seems like it could be really problematic in some cases in the future. I much prefer knowing exactly where the data comes from. But that may just be me.
Conclusion
In conclusion, use the Model approach if you know exactly what should be in the Form. Use the FormCollection approach if you really need to. That's pretty much it.
Good luck.

action for ajax.beginform throwing 404 from mvc app not from fiddler

I have the following and am getting a 404 on submit, but from fiddler I'm getting a 500 error about not having the antiforgerytoken. Any thoughts what might be wrong?
ShareController.cs code
// creates the form
[HttpPost]
public ActionResult ShareVideoFormEmail(string vguid)
{
var shareModel = GetShareVideoFormEmailModel(vguid);
return PartialView("ShareVideoFormEmail", shareModel);
}
// handler function
[HttpPost]
[ValidateHoneyPot]
[ValidateAntiForgeryToken]
public ActionResult ProcessShareVideoFormEmail(ShareVideoEmailModel model)
{
//do stuff
return PartialView("Result");
}
ShareVideoFormEmail.cshtml
#model Gcc.GctWebsite.Areas.Forms.Models.Share.ShareVideoEmailModel
#using (Ajax.BeginForm("ProcessShareVideoFormEmail","share",null ,new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "shareVideoFormWithMyFriends",
OnBegin = "onBeginShareEmail", #* show ajaxspinner *#
OnComplete = "onCompleteShareEmail", #* hide ajaxspinner *#
OnSuccess = "onSuccessShareEmail" #* hide ajaxspinner *#
}, new { id = "shareVideoEmailModelForm" }))
{
<div class="modal-header">
<div class="modal-header-title">Share the video "#ViewBag.VideoTitle" with my friends</div>
</div>
<div class="modal-body">
<div id ="shareVideoFormWithMyFriends" class="share-form-email">
#Html.AntiForgeryToken()
#Html.HoneyPot()
#Html.HiddenFor(x => x.UpdateTargetId)
#Html.HiddenFor(x => x.OgMetaTitle)
#Html.HiddenFor(x => x.OgMetaDescription)
#Html.HiddenFor(x => x.OgMetaDefaultImage)
#Html.HiddenFor(x => x.OgMetaImage)
#Html.HiddenFor(x => x.Brand)
#Html.HiddenFor(x => x.VGuid)
#Html.HiddenFor(x => x.TripCode)
#Html.HiddenFor(x => x.EmailSubject)
<!-- big section of input fields for model properties -->
<div class="form-field">
<span class="req-lbl">* = Required</span>
</div>
<div class="form-field form-submit">
<input class="btn btn-main-theme btn-narrow btn-submit-form" value="Send Email" type="submit">
<input type="reset" class="btn btn-grey btn-reset cancel-share-Email" value="Cancel">
</div>
<script>
$(document).ready(function (e) {
$('.required-input').after("<span class=\"rqdfield\">*</span>");
loadJqueryValidationManually("#shareVideoEmailModelForm");
});
</script>
It turns out that in my model in some of the data there were some html no-no's. I had to htmlencode a couple properties before displaying the view, then it worked fine upon postback.
I discovered this by changing the input parameter of the action method to object and removing the honeypot and xsfr validation. Then I got a nice 500 error explaining I had unsafe data.

Html.BeginForm Not work properly

I'm trying to use some features of MVC. In practice I made this view:
#using (Html.BeginForm("ResetPasswordToken", "Account", FormMethod.Post, new { rt = #Request.QueryString["rt"] })))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "Reset Password non riuscito")
<div class="container above-footer login-form">
<div class="col-md-6" align="center" style=" margin-left:25%; margin-top:100px; margin-bottom:100px;">
<div class="editor-label">
#Html.LabelFor(m => m.ResetToken)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.ResetToken, new { #Value = #Request.QueryString["rt"] })
#Html.ValidationMessageFor(m => m.ResetToken)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.NewPassword)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.NewPassword)
#Html.ValidationMessageFor(m => m.NewPassword)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.ConfirmPassword)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.ConfirmPassword)
#Html.ValidationMessageFor(m => m.ConfirmPassword)
</div>
<p>
<input type="submit" value="Change Password" />
</p>
</div>
</div>
The view is used to reset a password and i want to call the method ResetPasswordToken in AccountController when i click on "Change Password".
The AccountController is so structured, but it doesn't go to the method when i click on button:
[HttpPost]
public ActionResult ResetPasswordToken()
{
return View();
}
[HttpPost]
public ActionResult ResetPasswordToken(RecoverPasswordModel model)
{
if (ModelState.IsValid)
{
if (WebSecurity.ResetPassword(model.ResetToken, model.NewPassword))
{
return RedirectToAction("PasswordResetSuccess");
}
else
{
ModelState.AddModelError("", "The password reset token is invalid.");
}
}
return View(model);
}
Please someone can help me?
Thank you very much and good holidays
Roberto
You have two action with same name and same httppost attibute. Mark first action (without parameter) as httpget
Ok. It missing [AllowAnonymus]. Great and thank you. Now one question. What is the best method to pass the value of the tokens in the URL to the controller without use # Html.TextBoxFor (m => m.ResetToken, new { #Value = #Request.QueryString["rt"] }). I want to hidden this value to user.

Child actions are not allowed to perform redirect actions MVC4

Im very new to MVC and messing around. This has been driving me crazy all day, i have been reading around and it seems that i may have some fundamental errors in the structure of my code. But i am struggling to see what they are exactly. As far as i can see i am not even using a child action?
The error is being thrown on the return RedirectToAction line.
I have a registration form that i am displaying in a modal. This modal will be available to see on many pages - so i created an action that returns a partial view.
[AllowAnonymous]
public ActionResult RegisterPartial()
{
return PartialView(new RegisterModel());
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult RegisterPartial(RegisterModel model)
{
if (ModelState.IsValid)
{
//do stuff
return RedirectToAction("Data", "Settings", new { id = model.UserName });
}
// If we got this far, something failed, show form again
return PartialView(model);
}
This is called from my home page at the moment - but will be called from many pages eventually.
<div class="modal fade" id="signUpModal">
#Html.Action("RegisterPartial", "Account")
</div>
SignUp
And here is the partial view itself
#model Prog.Models.RegisterModel
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
<h4 class="modal-title">Signup</h4>
</div>
#using (Html.BeginForm())
{
<fieldset>
<div class="modal-body">
<p class="message-info">
</p>
#Html.ValidationSummary()
#Html.AntiForgeryToken()
<ol>
<li>
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName, new { #maxlength = "13" })
#Html.ValidationMessageFor(m => m.UserName)
</li>
<li>
#Html.LabelFor(m => m.email)
#Html.TextBoxFor(m => m.email)
#Html.ValidationMessageFor(m => m.email)
</li>
<li>
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password)
</li>
<li>
#Html.LabelFor(m => m.ConfirmPassword)
#Html.PasswordFor(m => m.ConfirmPassword)
</li>
#Html.ValidationMessageFor(m => m.ConfirmPassword)
</ol>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Post</button>
</div>
</fieldset>
}
</div></div>
Any help would be most appreciated! Thanks in advance :)
Turns out it was because i didnt fill out the html begin form statement correctly.
#using (Html.BeginForm("RegisterPartial", "Account", FormMethod.Post , new { #class = "form-horizontal" }))
this works now.
The problem is your POST action is returning PartialView if modelstate is invalid. So the partial view will show as entire page instead of model. So you can go with ajax forms. There is Ajax Helper available for MVC.
Include below script in your page.
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
Your view will be
#using (Ajax.BeginForm("RegisterPartial", "Account", new AjaxOptions() {
OnSuccess = "successCallback"})
{
}
<script>
function successCallback(response)
{
var username='#Model.UserName';
if(response)
{
windows.href.location='/Settings/Data/'+username;
}
}
</script>
Your action will be
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public JsonResultRegisterPartial(RegisterModel model)
{
if (ModelState.IsValid)
{
//do stuff
return Json(new {Success=true });
}
return Json(new {Success=false});
}
It might be helpful.

HTML.Partial() is not working when displaying it?

In Asp.net mvc 5. we have a login page which implements HTML.BeginForm() to post data to controller , if the username and password are incorrect, we are sending error back to the front end using partial views. Everything work fine, In case of error only error message is displayed on the screen and nothing else.
Controller
[HttpPost]
public ActionResult SingleSSOMPLogin(SSoStateModel model)
{
//Check the Code with SP providers and Authorization server.
if(model.SAMLAssertion.validMPData)
{
return RedirectToAction("SSOServiceMP" , "SAML");
}else
{
//Error message processed.
model.errorMessage = SSOState.SAMLAssertion.errorMessage;
return PartialView("_LoginError" , model);
}
}
The view contain the following Code
<div class="peripheral">
<div class="panel panel-default panel-peripheral">
#{Html.Partial("_LoginError", Model);}
<div class="panel-heading clearfix">Please Log In</div>
<div class="panel-body">
<div class="brand-logo eop-logo">
<script type="text/javascript">
function changeImage() {
document.getElementById("Logo").src = '#Url.Content("~/Content/images/TopLogo.gif")';
}
</script>
<img id="Logo" width="200" src='#Url.Content("~/Content/images/TopLogo.gif")' onerror="changeImage()" />
#{ Html.EnableClientValidation(true);}
<!--Ajax call to Controller-->
#using (Html.BeginForm("SingleSSOMPLogin", "Accounts"))
{
#Html.ValidationSummary(true);
<div id="group-email" class="form-group col-md-12 ">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control", placeholder = "Please enter your Email address" })
#Html.ValidationMessageFor(m => m.Email)
</div>
<div id="group-password" class="form-group col-md-12">
#Html.PasswordFor(m => m.Password, new { #class = "form-control", placeholder = "Please enter your password" })
#Html.ValidationMessageFor(m => m.Password)
</div>
<div class="form-group">
<div class="col-md-12">
<button type="submit" class="btn btn-primary pull-left">Login</button>
<a id="forgot" href='#Url.Action("ForgotPassword","Accounts")' class="btn btn-link btn-sm pull-right">Forgot your password?</a>
</div>
</div>
}
</div>
</div>
</div>
</div>
Partial View
#{
Layout = null;
}
#if (Model.Result!= null)
{
<div class="alert alert-warning alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<strong>#Model.Result.errorMessage</strong>
</div>
}
When Error occur, I get redirect to same view again with all query parameter gone and display only error message.
How to solve following issue?
The partial view will return only the fragment you defined.
So when called from a "complete" view with
#{Html.Partial("_LoginError", Model);}
it will generate the corresponding part of the view.
In your situation what is most common is to add a model error and return the complete view (that must have a ValidationSummary section):
[HttpPost]
public ActionResult SingleSSOMPLogin(SSoStateModel model)
{
//Check the Code with SP providers and Authorization server.
if(model.SAMLAssertion.validMPData)
{
return RedirectToAction("SSOServiceMP" , "SAML");
}else
{
//Error message processed.
ModelState.AddModelError("error", SSOState.SAMLAssertion.errorMessage);
return View(model);
}
}
If you want to use the partial view you have to call it from an javacript ajax call and insert the response in your page. With jQuery it is something more or less like:
$.ajax({ url: <partial view url>,
type: 'GET',
success: function (result) {
$(updateSelector).hide().html(result).effect("fade", "fast");
}
});

Categories

Resources