I have an old MVC website I'm looking at. I don't understand the following code in my Account controller. (I believe this was code generated by ASP.NET.)
Controller
//
// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code)
{
return code == null ? View("Error") : View();
}
//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
AddErrors(result);
return View();
}
View
#model OnBoard101.Models.ResetPasswordViewModel
#{
ViewBag.Title = "Reset password";
}
<h2>#ViewBag.Title</h2>
#using (Html.BeginForm("ResetPassword", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Reset your password</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Code)
<div class="form-group">
#Html.LabelFor(m => m.Email, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Password, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.ConfirmPassword, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.ConfirmPassword, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-success" value="Reset" />
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
These methods are called when the user uses the Forgot Password feature and then clicks the link that is sent to them in an email.
As best I can tell, the POST handler correctly detects an invalid code in the link (it produces an Invalid Token error when the code is not value). But I don't understand how. The GET handler seems to simply discard the code argument.
I don't get how this code works. How does model.Code ever get populated?
The ResetPassword feature does use code from the link.
Model binding retrieves data from various sources such as route data, form fields, and query strings.
It inspects the query string parameters for matching properties on the model used by the view
While it may appear like the code is being discarded by the Controller GET action, the code is still a part of the request and used by the view.
And since the view explicitly binds to a model
#model OnBoard101.Models.ResetPasswordViewModel
which has a matching public Code property (case-insensitive)
public string Code { get; set; }
it will bind it to the model in the view during the GET and then use it (the model) to populate a hidden form field (as shown in your View markup)
#Html.HiddenFor(model => model.Code)
So now when the form is submitted, the POST action will bind that field to the model posted back to the action and perform the validation
The same could have been achieved with
// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code) {
if (code == null) return View("Error");
var model = new ResetPasswordViewModel {
Code = code
};
return View(model);
}
But since the built-in model binding functionality would initialize a model if one was not provided, the above code really does not add anything additional to what the framework does out-of-the-box.
Related
How do I get this simple form validation going?
I have an AccountVerificationController which originally had the following method:
public ActionResult Index(AccountVerificationModel model)
Problem is when view is initially loaded, there are validation errors since the model has required fields as follows:
public class AccountVerificationModel
{
[Required]
public string MerchantId { get; set; }
[Required]
public string AccountNumber { get; set; }
[Required, StringLength(9, MinimumLength = 9, ErrorMessage = "The Routing Number must be 9 digits")]
}
However, this is not desired behavior. I want validation to occur only after user clicks on a validate button, so I changed form to invoke the Verify method in the account controller.
View is as follows;
#using (Html.BeginForm("Verify", "AccountVerification", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h2>#ViewBag.Title</h2>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.SubMerchantId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.MerchantId, new { htmlAttributes = new { #class = "form-control", placeholder = "MID" } })
#Html.ValidationMessageFor(model => model.MerchantId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.RoutingNumber, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.RoutingNumber, new { htmlAttributes = new { #class = "form-control", placeholder = "9 Digit Routing Number" } })
#Html.ValidationMessageFor(model => model.RoutingNumber, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input name="validate" type="submit" value="Validate" class="btn btn-info"/>
</div>
</div>
</div>
}
Now the challenge is handling model validation errors. I have the controller structured as follows:
public class AccountVerificationController : BaseMvcController
{
public AccountVerificationController()
{
}
public ActionResult Index()
{
return View(new AccountVerificationModel());
}
public ActionResult Verify(AccountVerificationModel model)
{
// do the validation then re-direct......
if (!model.IsValid())
{
return RedirectToAction("Index", model);
}
// otherwise try to validate the account
if (!model.VerificationSuccessful)
{
// repopulate the view with this model...
return RedirectToAction("Index", model);
}
return Redirect("Index");
}
However, during re-direction, I am losing the entire context, model errors and all. Reading upon this whole model binding but if someone can quickly spot what I am doing wrong here, that would be appreciated.
Well, you can't redirect when there's errors. Period. Redirection is actually a two-step process, even though it seems to happen seemlessly. First, the server returns a 301 or 302 status code with a header value indicating the URL the client should go to instead. Then, the client issues a brand new request to that given URL. That's why no context is persisted. It's basically the same as the client requesting the page for the very first time.
The only way to maintain the context is to simply return a view. Which means your Verify action would need to return the same view as your Index action. However, that's not exactly ideal. I don't exactly understand what the original problem was that made you decide to add the Verify action in the first place, but that can likely be fixed if you open a new question to that regard. You should really just postback to your Index action.
I have a big problem with MVC Telerik UI for ASP.NET
I am trying to get a checkbox up for a boolean field. I know we have two input fields to return the value false when the box is not touched.
When I do not touch the CBox, I get the value 'false' as expected. When I check the box, I get false too because the CBOx is returning a string = "true,false" which makes it impossible to convert directly to bool.
View
public class role
{
public string role_name { get; set; }
public bool add_school { get; set; }
}
Controller
public ActionResult test()
{
return View();
}
[HttpPost]
public async Task<ActionResult> test(Models.role role)
{
var z = Request["cb_addschool"];
var x = 1;
return RedirectToAction("Index");
}
View
#model Models.role
#using (Html.BeginForm("test", "Home", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h2>Add a New Role</h2>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.role_name, new { #class = "col-md-1 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.role_name, new { #class = "form-control form-control-big" })
</div>
</div>
<div class="form-group">
#Html.Kendo().CheckBoxFor(m=>m.add_school).Name("cb_addschool").Label("Add School")
</div>
<div class="form-group">
<div class="col-md-offset-3 col-md-9">
<input type="submit" class="btn btn-login" value="Register" />
</div>
</div>
}
Please, any help?
Remove this code from your action method:
var z = Request["cb_addschool"];
You have this value inside your role model. So this is pointless in this case.
Than remove this attribute from Kendo CheckBoxFor:
.Name("cb_addschool")
You don't have to need that (the property will be bound correctly without that).
Small hint: if you are using Kendo - use the Kendo().TextBoxFor method instead of #Html.TextBoxFor (or add "k-textbox" class to your TextBoxFor - it will use Kendo CSS styles).
Here is an example:
#(Html.Kendo().TextBoxFor(model => model.role_name)
.HtmlAttributes(new { placeholder = "Select role", #class = "form-control form-control-big" })
)
Can anyone tell me how to read the value of checked radio button from my View and save this value in database attribute type_of_user. the following is part of the view:
<div class="form-group">
<div class="control-label col-md-2">
Account Type: </div>
<div class="col-md-10">
#Html.RadioButton("AccountType", "Individual", true) Individual
#Html.RadioButton("AccountType", "Organization", false) Organization
#Html.ValidationMessageFor(model => model.Type_of_user)
</div>
</div>
and in The controller I placed this code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include="Username,Password,Status,Email,Type_of_user,admin_id")] Account account)
{
if (ModelState.IsValid)
{
db.Accounts.Add(account);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.admin_id = new SelectList(db.Adminstrators, "Admin_ID", "Phone", account.admin_id);
return View(account);
}
I have a IdentityUser Model
I have a Manage View with 3 partials (each one have one viewmodel and controller) I want to enter on this view and see the forms with data filled of the model.
ApplicationUser : IdentityUser (Model of my user)
using Microsoft.AspNet.Identity.EntityFramework;
using System;
namespace MTGWeb.Models
{
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
public String Pais;
public String Email;
public DateTime UltimoLogin;
public DateTime FechaRegistro;
public String Tipo;
public Boolean Activado;
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
}
}
Manage (Main View)
#using MTGWeb.Models;
#using Microsoft.AspNet.Identity;
#{
ViewBag.Title = "Administrar cuenta";
}
<h2>#ViewBag.Title.</h2>
<p class="text-success">#ViewBag.StatusMessage</p>
<div class="row">
<div class="col-md-12">
<p>Ha iniciado sesión como <strong>#User.Identity.GetUserName()</strong>.</p>
#Html.Partial("_ChangePasswordPartial")
#Html.Partial("_ChangeEmailPartial")
#Html.Partial("_OtherFieldsPartial")
</div>
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
_ChangeEmailPartial
#using Microsoft.AspNet.Identity
#model MTGWeb.Models.ManageUserViewModelEmailChange
#using (Html.BeginForm("ManageEmail", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Formulario para cambiar email</h4>
<hr />
#Html.ValidationSummary()
<div class="form-group">
#Html.LabelFor(m => m.OldEmail, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.OldEmail, new { #class = "form-control", Value = Model.OldEmail})
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.NewEmail, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.NewEmail, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.ConfirmEmail, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.ConfirmEmail, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Cambiar email" class="btn btn-default" />
</div>
</div>
}
Controller - ManageEmail
// Cambia el email
// POST: /Account/ManageEmail
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ManageEmail(ManageUserViewModelEmailChange model)
{
ViewBag.ReturnUrl = Url.Action("Manage");
var user = await UserManager.FindByNameAsync(User.Identity.Name);
if (ModelState.IsValid)//Si no hay errores en la validación que hace la clase (Datatype, length, required, etc..)
{
if (model.OldEmail.Equals(model.NewEmail) && model.OldEmail.Equals(user.Email))
{
user.Email = model.NewEmail;
IdentityResult success = await UserManager.UpdateAsync(user);
if (success.Succeeded)
{
return RedirectToAction("Manage", new { Message = ManageMessageId.ChangeEmailSuccess });
}
}
}
//Si el modelo no es válido o no el cambio no ha tenido exito
return View(model);
}
I have 2 more controllers for others partials, but this is usefull for the example. These Model.OldEmail is null and causes a Nullreference error, Where I have to fill it? I guess that this have to be filled in AccountController/Manage, but.. How can I send it to the partials?
I am new on MVC and .NET, I used to work with Java, and I am stucked in this (Is a testing project purposes)
pass the model you want the partials to show into the partials as an argument
you will need to add a viewmodel containing the models you want to show to the host view.
eg
#Html.Partial("_ChangePasswordPartial",Model.ChangePAsswordViewModel)
Model is a property on Controller (which your manage controller will inherit from)
You pass the viewmodel into the manage view from the controller in return View(YourViewModelInstance) from your Manage controller method.
you also need to add a reference to that model in your manage form like you have in your partials
Eg
#model MTGWeb.Models.ManageViewModel
your manage viewmodel might look something like
public class ManageViewModel
{
public ChangePasswordViewModel ChangePasswordViewModel{get;set;}
public NextViewModel NextViewModel{get;set;}
public AnotherViewModel NextViewModel{get;set;}
}
I'm having a problem in my MVC project. When trying to create an object to add it to the db, it always returns null.
public class ListsModel
{
public EntitiesList EntityList { get; set; }
public List<string> AllGroups { get; set; }
}
public ActionResult Create()
{
ListsModel model = new ListsModel();
model.EntityList = new EntitiesList();
model.AllGroups = managerLists.GetAllListsKeys(); //For droplist
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ListsModel model)
{
if (ModelState.IsValid)
{
model.EntityList.List_CreatedTime = DateTime.Now;
managerLists.AddNewObject(model.EntityList);
return RedirectToAction("Index");
}
return View(model);
}
And a simple cshtml:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>EntitiesList</legend>
<div class="form-group">
#Html.LabelFor(model => model.EntityList.List_EntitityName)
#Html.DropDownListFor(model => model.AllGroups, new SelectList(Model.AllGroups),
new { #class = "form-control" })
<p class="help-block">#Html.ValidationMessageFor(model => model.EntityList.List_EntitityName)</p>
</div>
<div class="form-group">
#Html.LabelFor(model => model.EntityList.List_EntityValue)
<input class="form-control" value="#Model.EntityList.List_EntityValue"/>
<p class="help-block">#Html.ValidationMessageFor(model => model.EntityList.List_EntityValue)</p>
</div>
<div class="form-group">
#Html.LabelFor(model => model.EntityList.List_OrderByNumber)
<input class="form-control" value="#Model.EntityList.List_OrderByNumber"/>
<p class="help-block">#Html.ValidationMessageFor(model => model.EntityList.List_OrderByNumber)</p>
</div>
<div class="form-group">
#Html.LabelFor(model => model.EntityList.List_Comments)
<textarea class="form-control" rows="3">#Model.EntityList.List_Comments</textarea>
<p class="help-block">#Html.ValidationMessageFor(model => model.EntityList.List_Comments)</p>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
When it's getting to the "model.EntityList.List_CreatedTime = DateTime.Now;" a null reference exception is thrown.
I tried to change the signature to "public ActionResult Create(ListsModel ListsModel)", as suggested here: Create view is posting null objects
But I got the same results.
Hope you can help me.
I think the problem is the way you define inputs like this:
<input class="form-control" value="#Model.EntityList.List_EntityValue"/>
For ASP MVC can collect form data, inputs should have an Name attribute corresponding with model fields.
Try to generate inputs using the standard:
#Html.TextBoxFor(model => model.EntityList.List_EntityValue)
I suggest you inspect the differences in the html generated (to see how is asp mvc generating inputs).