Adding a variable into the HTML.BeginForm Model - c#

I am having issues retaining the passwordToken between my GET Controller and my View. I see that the token is passed and added to the model correctly within the GET Controller but as soon as the HTML.BeginForm starts in the View the model has a new instance and the previous model with the passwordToken is lost. I need the passwordToken to be retained in order to use WebSecurity.ResetPassword. Any suggestions on how this could be done?
My GET Controller:
[AllowAnonymous]
public ActionResult PasswordReset(string passwordToken)
{
// Token Validation
var usrID = WebSecurity.GetUserIdFromPasswordResetToken(passwordToken);
var usr = _dbManager.GetUserInformation(usrID);
if (usr == null)
{
//The link you are using is not valid anymore
return RedirectToAction("Error", "Account");
}
else
{
var model = new PasswordReset();
model.PasswordResetToken = passwordToken;
return View(model);
}
}
My View:
#model Project.Models.PasswordReset
#{
ViewBag.Title = "Password Reset";
}
<h2>Password Reset</h2>
<div class="form passwordreset-form">
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div class="input-form">
<div class="inputbox-label">
#Html.LabelFor(m => m.Password)
</div>
<div class="inputbox">
#Html.PasswordFor(m => m.Password)
</div>
<div class="inputbox-label">
#Html.LabelFor(m => m.ConfirmPassword)
</div>
<div class="inputbox">
#Html.PasswordFor(m => m.ConfirmPassword)
</div>
</div>
<div style="float:right;">
<input type="submit" value="Change Password" />
</div>
}
</div>
My POST Controller:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult PasswordReset(PasswordReset model)
{
//Attemp to change password
var passwordChangeConfirmation = WebSecurity.ResetPassword(model.PasswordResetToken, model.Password);
//Password has been changed
if(passwordChangeConfirmation == true)
{
return RedirectToAction("Index", "Home");
}
//Password change has failed
else
{
return RedirectToAction("Error", "Account");
}
}

I ended up adjusting the POST class to make it work.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult PasswordReset(PasswordReset model, string passwordToken)
{
//Attemp to change password
model.PasswordResetToken = passwordToken;
var passwordChangeConfirmation = WebSecurity.ResetPassword(model.PasswordResetToken, model.Password);
//Password has been changed
if (passwordChangeConfirmation == true)
{
return RedirectToAction("Index", "Home");
}
//Password change has failed
else
{
return RedirectToAction("Error", "Account");
}
}

add it into your form:
#Html.HiddenFor(m => m.PasswordResetToken);

You can use a hidden input on the form for the field (from your model) that you pass it.
#Html.HiddenFor(m => m.PasswordResetToken);
in output
<input type="hidden" name="PasswordResetToken"></input>

Related

ASP.NET MVC: Method for HttpPost is not executed

In an ASP.NET MVC application, I have a file MessageController.cs where I define EditMessage for access via HttpGet and HttpPost. Usually the user first accesses it via HttpGet, then a form pops up where he can edit the message, and then he clicks on the Save button, by which HttpPost will be invoked.
My problem is that HttpPost is not invoked. Instead, an error message is displayed. I have analogous code for modifying other parts of the database and with that analogous code, HttpPost works. My question is why it does not work here and what I can do to make it work.
/// <summary>
/// used by admin only
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[CustomAuthorization(new[] { GlobalStaticFunc.SecurityOptions.isSUser, GlobalStaticFunc.SecurityOptions.isAdmin })]
[HttpGet]
public async Task<ActionResult> EditMessage(int id)
{
if (PopupController.AnyPopupsInline(User))
{
return RedirectToAction("index", "popup");
}
if (id > 0)
{
BLogic_Entity dbContext = new VMIEntityCreator(true);
var msg = dbContext.GetDbSet<MSG_MESSAGES>().Where(x => x.id == id).FirstOrDefault();
if (msg != null) return View(msg);
}
else if (id == -1)
{
return View(new MSG_MESSAGES() { id = -1 });
}
return View("Messages");
}
[CustomAuthorization(new[] { GlobalStaticFunc.SecurityOptions.isCarrier, GlobalStaticFunc.SecurityOptions.isAdmin, GlobalStaticFunc.SecurityOptions.isSUser })]
[HttpPost]
// [ValidateAntiForgeryToken]
public async Task<ActionResult> EditMessage(MSG_MESSAGES model)
{
if (PopupController.AnyPopupsInline(User))
{
return RedirectToAction("index", "popup");
}
if (!App_Tools.RightsHandler.IdentityWatcher.CheckUserRights(User.Identity, GlobalStaticFunc.SecurityOptions.isAdmin) && App_Tools.RightsHandler.IdentityWatcher.CheckUserRights(User.Identity, GlobalStaticFunc.SecurityOptions.isEndUser))
{
return RedirectToAction("Messages", "Message");
}
bool isOk = false;
if (model != null)
{
if (!ModelState.IsValid)
{
return View(model);
}
if (model.id > 0)
{
using (TED_BLOGIC.Abstractions.BLogic_Entity usr = new VMIEntityCreator(true))
{
isOk = await usr.UpdateSecurely(usr.GetDbSet<MSG_MESSAGES>().Where(x => x.id == model.id).FirstOrDefault(), model, ModelState);
}
}
}
return View(model);
}
The code of EditMessage.cshtml:
#model TED_BLOGIC.DataBase.DB_MODEL.MSG_MESSAGES
#{
if (Model != null && Model.id > 0)
{
ViewBag.Title = "Message bearbeiten";
}
else
{
ViewBag.Title = "Neue Message anlegen";
}
ViewBag.Ico = "fa-plus";
Layout = "~/Views/Shared/_standardBoxView.cshtml";
}
#using (Html.BeginForm("EditMessage", "Message", new { id = Model.id }, FormMethod.Post, new { data_goback = true }))
{
#Html.AntiForgeryToken()
<div class="panel-body">
<div class="row">
<div class="col-md-12 table-responsive">
#Html.ValidationSummary(false, "", new { #class = "text-danger" })
#Html.EditorForModel("Add/MGV")
<div class="section row mb10">
<div class="form-group">
<div class="col-md-offset-2 col-lg-3 col-md-4 col-sm-5">
<input type="submit" value="Save" class="btn btn-default" onclick=";" /> #*mCust.postToSite(#Url.Action("User", "Admin"));mCust.sendMeBack()*#
</div>
</div>
</div>
</div>
</div>
</div>
}
<script src="~/Scripts/Core/PostbackHandling/OverwritePostbacks.js?v=#GlobalStaticFunc.Version"></script>
<script>
$(document).on("EverythingIsReady", function () {
document.title = 'Cloud - #ViewBag.Title';
})
</script>
<input type="submit" value="Save" class="btn btn-default" onclick=";" />
Could you please try removing onclick attribute of the button?
please check by commenting CustomAuthorization attribute above that post method, the post-event will be fired. I tested your code.

Unable to edit IdentityRole

I have the following view:
#model Microsoft.AspNet.Identity.EntityFramework.IdentityRole
#{
ViewBag.Title = Resources.Edit;
}
<h2>#Resources.EditRole</h2>
#Html.ActionLink(Resources.ListRoles, "Index") | #Html.ActionLink(Resources.ManageUserRoles, "ManageUserRoles")
<hr />
<div class="row">
<div class="col-md-8">
<section id="editRoleForm">
#using (Html.BeginForm("Edit", "Role", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>#Resources.Role</h4>
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(m => m.Name, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="#Resources.Save" class="btn btn-default" />
</div>
</div>
}
</section>
</div>
</div>
I also have the following two methods in my RoleController:
//
// GET: /Role/Edit/5
public ActionResult Edit(string Role)
{
var thisRole = context.Roles.Where(r => r.Name.Equals(Role, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
return View(thisRole);
}
//
// POST: /Role/Edit/5
[HttpPost]
public ActionResult Edit(FormCollection collection)
{
try
{
var thisRole = context.Roles.Where(r => r.Id.Equals(collection["Id"])).FirstOrDefault();
thisRole.Name = collection["Name"];
context.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
Originally, I was trying to use this method instead of the second one:
//
// POST: /Role/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(IdentityRole Name)
{
try
{
context.Entry(Name).State = System.Data.Entity.EntityState.Modified;
context.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
But I never got it to work because the Name parameter was always null -which I still don't know why it happened, so if someone can explain it to me that'll be greatly appreciated.
I wrote then the other method since I saw the use of FormCollection in another example (to create roles) and it seems to work fine, at least it contains the information I need when I debug it. My issue is that although collection["id"] has the right Id for the Role I'm trying to edit, context.Roles is completely empty. This makes no sense to me given that when the first method is called (loading the View for the first time), this line
var thisRole = context.Roles.Where(r => r.Name.Equals(Role, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
returns the selected role (out of the several that exist and that I can see when I add to watch context.Roles). However, after the view is loaded, the textbox edited and the second method in the controller gets called, context.Roles has nothing in it. Why?
Ok you can use the already built in [Authorize(Roles="RoleType")] filter.
You then have your regular User model and Account controller so you can then authorize users. Once you have authorised them you can set them to a specific role.
E.g. user story: only admins can access action result x
[Authorize(User="Admin")]
Public ActionResult X(){
...
}
That way you simply assign user roles in the model creation.
E.g.
Public Class UserModel
{
int id {get;set;}
string name {get;set;}
string Role {get;set;}
.....
}
Now only users that have been authorised AND are of Role type "Admin" will be able to access that controller.
If you want to edit their role you can do a simple edit user action method
e.g.
[Post]
public actionresult edituser(int? id)
{
if (id == null)
{
return HttpNotFound();
}
using (var db = new UserContext())
{
UserModel editUser = db.UserModel.Find(id);
if (editUser == null)
{
return HttpNotFound();
}
db.User(editModel).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
}
RedirectToAction("Action", "Controller");
}
Now any user that is NOT of Role type "Admin" will not be able to access that screen. They will receive a 404 error.

ASP.NET MVC 5 HttpPost

I have controller:
[HttpGet]
public ActionResult ItemIndex()
{
List<Item> item = RepositoryFactory.Create<IItemRepository>().ItemList();
return View(item);
}
[HttpPost]
public ActionResult ItemIndex(FormCollection formCollection)
{
foreach( string key in formCollection.AllKeys)
{
Response.Write("Key" + key);
Response.Write(formCollection[key]);
}
return View();
}
And view for that:
#model List<Kev.Models.Item>
<div style="font-family:Arial">
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
foreach (var item in Model)
{
#Html.LabelFor(m => item.Start)
#Html.EditorFor(m => item.Start)
#Html.ValidationMessageFor(m => item.Start)
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Zapisz" class="btn btn-default" />
</div>
</div>
}
</div>
Repository have DbContext with Item inside.
And what i want to do is fill this textboxes in View, and submit changes to update existing DataBase with this values.
What i did for now is not working code, i cant even make this HttpPost work.
It popup NullReferenceException for >>Model inside #foreach and not sure how to fix that.
Change your foreach to for loop like this:
for(int i=0; i<Model.Count;i++)
{
#Html.LabelFor(m => Model[i].Start)
#Html.EditorFor(m => Model[i].Start)
#Html.ValidationMessageFor(m => Model[i].Start)
}
and change your action parameter form FormCollection to List<Item>:
[HttpPost]
public ActionResult ItemIndex(List<Kev.Models.Item> model)
{
return View();
}

Not sure how to connect an HTML input button to a new ActionResult function

I'm very new to Razor MVC and I can't figure out how to wire an HTML input element click function to an ActionResult. Here is my code:
I took this code from a Login.cshtml file in the project and put it in Index.cshtml:
#model S2GPortal.Models.LoginModel
.
.
.
<section id="featured">
<h2>Use a local account to log in.</h2>
#using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl })) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Log in Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName)
#Html.ValidationMessageFor(m => m.UserName)
</li>
<li>
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password)
#Html.ValidationMessageFor(m => m.Password)
</li>
<li>
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(m => m.RememberMe, new { #class = "checkbox" })
</li>
</ol>
<input type="submit" value="Log in" />
</fieldset>
<p>
#*#Html.ActionLink("Register", "Register")*# if you don't have an account.
</p>
}
</section>
This would, in turn, call a Login ActionResult method on an AccountController controller. When it was in the Login view. Since I've put it in the Index view, the Login method no longer gets called, and I can't figure out how to rewire it to look in the same controller. I'm not sure how MVC knew to call that specific Login ActionResult before. Here is the controller:
public class AccountController : BaseController
{
//
// GET: /Account/Login
[Inject]
public ISystemUserRepository SystemUserRepository { get; set; }
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
if (WebSecurity.IsAuthenticated)
{
string currentUser = WebSecurity.CurrentUserName;
int test = 1;
}
ViewBag.ReturnUrl = returnUrl;
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
PortalSession.User = SystemUserRepository.GetByEmail(model.UserName);
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
So, In summary, How does a view know to look into a specific controller for an ActionResult to call, and how does a line like this: know which ActionResult to call?
Thanks!
you have to use this overload of Html.BeginForm:
#using (Html.BeginForm("Login", "Account", FormMethod.Post, new { ReturnUrl = ViewBag.ReturnUrl }))
{
// your form elements
}
1) First Parameter is ActionName
2) Second Controller Name
3) Third Form Method in your case it will be post as we are posting data
4) and fourth is route values
your final view will be like this:
#model S2GPortal.Models.LoginModel
<section id="featured">
<h2>Use a local account to log in.</h2>
#using (Html.BeginForm("Login", "Account", FormMethod.Post, new { ReturnUrl = ViewBag.ReturnUrl }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Log in Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName)
#Html.ValidationMessageFor(m => m.UserName)
</li>
<li>
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password)
#Html.ValidationMessageFor(m => m.Password)
</li>
<li>
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(m => m.RememberMe, new { #class = "checkbox" })
</li>
</ol>
<input type="submit" value="Log in" />
</fieldset>
}
</section>
Now on click of submit button your form will be posted on Login Action of Account Controller and rememeber form will only be posted with <input type="submit"/>, button or link will not work.

Query string param is missed when form validation fail

I have an form with following url:
CreateEntity?officeCodeId=5
When I send form to validate and if validation is fail it returns just CreateEntity url. No officeCodeId=5.
if user click enter on URL or F5 - my site fail - it require missing officecodeId param. I can save it to the session or in the other storage. But I want to have it in the URL
My view:
[HttpGet]
public virtual ActionResult CreateEntity(int? officeCodeId)
{
var model = new CreateViewModel();
FillViewModel(model, officeCodeId);
return View("Create", model);
}
[HttpPost]
protected virtual ActionResult CreateEntity(TEditViewModel model)
{
if (ModelState.IsValid)
{
//Do some model stuff if
}
return View("Create", model);
}
EDIT.
My View:
using (Html.BeginForm("CreateEntity", "Employee", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.HiddenFor(x => x.OfficeCodeId)
<div>
#Html.LabelFor(model => model.FirstName, CommonRes.FirstNameCol)
#Html.TextBoxFor(model => model.FirstName, Model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div>
#Html.LabelFor(model => model.LastName, CommonRes.LastNameCol)
#Html.TextBoxFor(model => model.LastName, Model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</div>
<div> <div class="input-file-area"></div>
<input id="Agreements" type="file" name="Agreements"/>
</div>
}
Edit 2.
Adding:
#using (Html.BeginForm("CreateEntity", "Employee", FormMethod.Post, new { officeCodeId = Model.OfficeCodeId, enctype = "multipart/form-data" }))
Haven`t help.
It produce the following form:
<form action="/PhoneEmployee/CreateEntity" enctype="multipart/form-data" method="post" officecodeid="5">
Solution Is
<form action="#Url.Action("CreateEntity", "Employee")?officecodeid=#Model.OfficeCodeId" enctype="multipart/form-data" method="post">
The problem is your HttpPost action doesn't have any notion of an id parameter. If you want to support a similar URL then make the action signature support that parameter e.g.
[HttpGet]
public ActionResult CreateEntity(int? officeCodeId)
[HttpPost]
public ActionResult CreateEntity(int officeCodeId, EditViewModel model);
Your actions should looks like this:
Actions:
[HttpGet]
public virtual ActionResult CreateEntity(int? officeCodeId)
{
var model = new CreateViewModel();
FillViewModel(model, officeCodeId);
return View("Create", model);
}
[HttpPost]
public virtual ActionResult CreateEntity(ViewModel model)
{
if (model.IsValid) {
// save...
return RedirectToAction("EditEntity", newId!!!);
}
return View("Create", model);
}
Html:
#using (Html.BeginForm()) {
#Html.HiddenFieldFor(m => Model.officeCodeId)
...
}
Your officeId should be in model. And on html form you can store it in hidden field.
Your final answer is excellent and works great, although you can further enhance it to make it more generic by simply including Request.QueryString:
<form action="#Url.Action("CreateEntity", "Employee")?#(Request.QueryString)"
enctype="multipart/form-data" method="POST">
Then use the POST action:
[HttpPost]
protected virtual ActionResult CreateEntity(TEditViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}

Categories