Validate Email uniqueness on custom edit profile form
I'm trying to do something that seems relatively simple on the surface but I'm finding myself going down a rabbit hole. I'm developing a custom Edit Profile form for members and I'm trying to validate that their email address (if they choose to change it) is not already in use before I update the membership record.
Apologies for my obtuse code. It's rapidly deteriorating as I'm working on finding a solution that works.
I have a view that inherits from UmbracoViewPage. In this view I call Html.Action() invoking a ChildActionOnly method on my surface controller.
In the ChildActionOnly method I'm retrieving the current member's profile via Members.GetCurrentMemberProfileModel() and using that ProfileModel to initialize the properties on my custom view model. Then I return a PartialView to display the custom form using the custom view model.
[ChildActionOnly]
public ActionResult MemberEditProfileRenderForm()
{
// code that builds model ommitted
return PartialView("Profile/MemberDetailsForm", model);
}
Originally I had the validation logic in the surface controller method that handles the form post but I found that the ModelState error that I was adding manually wasn't persisted for some reason.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult HandleEditMemberDetails([Bind(Prefix = "memberProfileViewModel")] MemberProfileViewModel model)
{
if (!ModelState.IsValid)
return CurrentUmbracoPage();
var profileModel = Members.GetCurrentMemberProfileModel();
// If the Email has changed verify that there isn't another member with that Email in the system.
if (!profileModel.Email.Equals(model.Email, StringComparison.OrdinalIgnoreCase))
{
var duplicateEmailMember = Members.GetByEmail(model.Email);
if (duplicateEmailMember != null)
{
//The following doesn't work for some reason
ModelState.AddModelError("memberProfileViewModel.Email", Umbraco.GetDictionaryValue("A member with this e-mail address already exists", "A member with this e-mail address already exists"));
return CurrentUmbracoPage();
}
}
So now I'm trying to code a custom validation attribute and apply it to my view model but the problem I'm running into is I don't know how to DI the MembershipHelper in the attribute constructor properly. Here's my current code for the validator.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Umbraco.Web.Security;
namespace ICSA.Core.Models.Profile
{
public class UniqueEmail : ValidationAttribute
{
private MembershipHelper membershipHelper;
public UniqueEmail(MembershipHelper memHelper)
{
membershipHelper = memHelper;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var model = (MemberProfileViewModel)validationContext.ObjectInstance;
var profileModel = membershipHelper.GetCurrentMemberProfileModel();
// If the Email has changed verify that there isn't another member with that Email in the system.
if (!profileModel.Email.Equals(model.Email, StringComparison.OrdinalIgnoreCase))
{
var duplicateEmailMember = membershipHelper.GetByEmail(model.Email);
if (duplicateEmailMember != null)
{
return new ValidationResult("A member with this e-mail address already exists", new List<string>() { "memberProfileViewModel.Email" });
}
}
return ValidationResult.Success;
}
}
}
But now I've realized I don't believe it's possible for me to pass in a reference to the Membership helper in a validation attribute constructor. The following isn't valid syntax.
[Required(AllowEmptyStrings = false, ErrorMessage = "Please provide your email address")]
[EmailAddress]
// membershipHelper isn't a valid attribute parameter type
[UniqueEmail(membershipHelper)]
public string Email { get; set; }
At this point I'm pretty sure that I'm missing something relatively simple in my original code where I'm calling ModelState.AddModelError() in the surface controller method. I'm just not sure what's preventing that validation error from appearing in the ValidationMessageFor() in my view.
[System.Web.Mvc.Remote("CheckForEmailAddress", "MemberSurface", ErrorMessage = "The email address is already in use")]
public string Email { get; set; }
which calls a surface action that returns Json to the UmbracoForm
public JsonResult CheckForEmailAddress(string email)
{
var emailcheck = Members.GetByEmail(email);
if (emailcheck != null)
{
return Json("This Email Address is already registered", JsonRequestBehavior.AllowGet);
}
else
{
return Json(true, JsonRequestBehavior.AllowGet);
}
}
Validate Email uniqueness on custom edit profile form
I'm trying to do something that seems relatively simple on the surface but I'm finding myself going down a rabbit hole. I'm developing a custom Edit Profile form for members and I'm trying to validate that their email address (if they choose to change it) is not already in use before I update the membership record.
Apologies for my obtuse code. It's rapidly deteriorating as I'm working on finding a solution that works.
I have a view that inherits from UmbracoViewPage. In this view I call Html.Action() invoking a ChildActionOnly method on my surface controller.
In the ChildActionOnly method I'm retrieving the current member's profile via Members.GetCurrentMemberProfileModel() and using that ProfileModel to initialize the properties on my custom view model. Then I return a PartialView to display the custom form using the custom view model.
Originally I had the validation logic in the surface controller method that handles the form post but I found that the ModelState error that I was adding manually wasn't persisted for some reason.
So now I'm trying to code a custom validation attribute and apply it to my view model but the problem I'm running into is I don't know how to DI the MembershipHelper in the attribute constructor properly. Here's my current code for the validator.
But now I've realized I don't believe it's possible for me to pass in a reference to the Membership helper in a validation attribute constructor. The following isn't valid syntax.
At this point I'm pretty sure that I'm missing something relatively simple in my original code where I'm calling ModelState.AddModelError() in the surface controller method. I'm just not sure what's preventing that validation error from appearing in the ValidationMessageFor() in my view.
I decorate my ViewModel property with
which calls a surface action that returns Json to the UmbracoForm
Thank you very much. I did not know about the Remote attribute. That is exactly what I was looking for and works like a charm.
Yes it's a handy decorator if you need to do any custom validation
is working on a reply...