MVC 4 Add Profile Image to RegisterModel - asp.net-mvc

How do you add the option to upload a profile image to the default RegisterModel in MVC 4?

This answer converts the image to a byte array so that you can then save it in a database. It can easily be modified if you wanted to save the image to file store.
The code for the View Model. The important part is the multipart/form-data attribute:
#using (Html.BeginForm("Register", "Account", null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset>
<legend>Registration Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName)
</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>
<li>
<label for="register-avatar">Upload your photo</label>
<input id="register-avatar" type="file" name="ProfileImage" />
</li>
</ol>
<input type="submit" value="Register" />
</fieldset>
}
The RegisterModel:
public class RegisterModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public HttpPostedFileBase ProfileImage { get; set; }
}
The AccountController's HTTPPost for the Register.cshtml View:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
WebSecurity.Login(model.UserName, model.Password);
MemoryStream target = new MemoryStream();
model.ProfileImage.InputStream.CopyTo(target);
byte[] data = target.ToArray();
var profileImage = new ProfileImage();
profileImage.Data = data;
profileImage.MimeType = model.ProfileImage.ContentType;
/// other code to save the image to the database
return RedirectToAction("Index", "Profile/" + model.UserName);
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
This is a quick run down of how I managed to upload a profile image along with the registration built into the MVC 4 template.

Related

Validation Error not being displayed (MVC4 and EF)

The invalid login error message is not being displayed for incorrect username or password. I have a Model called User and a Controller with the Action Method Validate, which validates the username and password. Upon successful validation I redirect to Create Action method, if not I add a model error and I want to display an "Invalid username or password" message on the login screen.
Model:
public class User
{
public int ID { get; set; }
[Required]
[Display(Name="User Name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required]
[Display(Name="First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name="Last Name")]
public string LastName { get; set; }
[Required]
[DataType(DataType.PhoneNumber)]
[MinLength(10)]
[MaxLength(10)]
[Display(Name="Mobile No")]
public string PhoneNum { get; set; }
}
Controller:
[HttpGet]
public ActionResult Validate()
{
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Validate(User user)
{
var u1 = db.Users.Where(p => p.UserName == user.UserName && p.Password == user.Password).FirstOrDefault();
if (u1 != null)
{
return RedirectToAction("Create");
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
return RedirectToAction("Validate");
}
View:
#model HindiMovie.Models.User
#{ViewBag.Title = "Login";}
<h2>Login</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(false,"The user name or password provided is incorrect.")
<fieldset>
<legend>User</legend>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Password)
</div>
<div class="editor-field">
#Html.PasswordFor(model => model.Password)
#Html.ValidationMessageFor(model => model.Password)
</div>
<p>
<input type="submit" value="Validate" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Redirecting resets the ModelState. You probably want to re-display the view instead:
public ActionResult Validate(User user)
{
var u1 = db.Users.Where(p => p.UserName == user.UserName && p.Password == user.Password).FirstOrDefault();
if (u1 != null)
{
return RedirectToAction("Create");
}
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View();
}

using a custom attribute on viewmodel httpfilebase property to check file extensions not working

I'm using .net 4.0 so can't utilize the built in FileExtensions attribute.
I'm trying to roll up my own validation but running into a stumbling block. I found this article which looks to be a good resource: http://blog.tomasjansson.com/creating-custom-unobtrusive-file-extension-validation-in-asp-net-mvc-3-and-jquery
but alas, my object (value) is always coming in as null. the only thing I'm doing differently is I'm not using the HttpPostedFileBase as my model, I'm using my viewmodel which has a few other properties.
any ideas how I can populate my object in the IsValid overload so that I can check it?
Here is my code which is more or less a copy and paste from that article with the exception that I've got more in my viewmodel:
ViewModel:
public class Mp3ViewModel
{
public string FileName { get; set; }
public string FilePath { get; set; }
[Required(ErrorMessage="You must enter a description of the MP3")]
[Display(Name = "Description of MP3:")]
public string Description { get; set; }
[Required(ErrorMessage = "You must enter a job role")]
[Display(Name = "Job Role:")]
public string CallJobRole { get; set; }
[Required(ErrorMessage = "You must enter a call outcome")]
[Display(Name = "Call Outcome:")]
public string CallOutcome { get; set; }
[Required(ErrorMessage = "You must enter a call type")]
[Display(Name = "Call Type:")]
public string CallType { get; set; }
[Required(ErrorMessage = "You must enter a call section")]
[Display(Name = "Call Section:")]
public string CallSection { get; set; }
[Required(ErrorMessage = "You must enter call comments")]
[Display(Name = "Call Comments:")]
public string CallComments { get; set; }
[Required(ErrorMessage = "You must enter call keywords")]
[Display(Name = "Call Keywords (separate by comma):")]
public string CallKeywords { get; set; }
[Required(ErrorMessage = "You must select a file")]
[Display(Name = "Select an MP3 to upload:")]
[FileExtensions("txt|doc")]
public HttpPostedFileBase Mp3 { get; set; }
}
Custom Attribute Validation:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class FileExtensionsAttribute : ValidationAttribute
{
private List<string> ValidExtensions { get; set; }
public FileExtensionsAttribute(string fileExtensions)
{
ValidExtensions = fileExtensions.Split('|').ToList();
}
public override bool IsValid(object value)
{
HttpPostedFileBase file = value as HttpPostedFileBase;
if (file != null)
{
var fileName = file.FileName;
var isValidExtension = ValidExtensions.Any(y => fileName.EndsWith(y));
return isValidExtension;
}
return true;
}
}
View:
#model CallLibrary.BO.ViewModels.Mp3ViewModel
#{
ViewBag.Title = "Call Library Administration";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#section scripts
{
<script src="~/Scripts/jquery.validate.min.js" type="text/javascript"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js" type="text/javascript"></script>
}
<h2>Call Library Administration</h2>
#ViewBag.test
#using (Html.BeginForm())
{
<div>
#Html.ValidationMessageFor(x => x.Description)<br />
#Html.LabelFor(x => x.Description)
#Html.TextAreaFor(x => x.Description)
</div>
<div>
#Html.ValidationMessageFor(x => x.CallJobRole)<br />
#Html.LabelFor(x => x.CallJobRole)
#Html.TextBoxFor(x => x.CallJobRole)
</div>
<div>
#Html.ValidationMessageFor(x => x.CallOutcome)<br />
#Html.LabelFor(x => x.CallOutcome)
#Html.TextBoxFor(x => x.CallOutcome)
</div>
<div>
#Html.ValidationMessageFor(x => x.CallType)<br />
#Html.LabelFor(x => x.CallType)
#Html.TextBoxFor(x => x.CallType)
</div>
<div>
#Html.ValidationMessageFor(x => x.CallSection)<br />
#Html.LabelFor(x => x.CallSection)
#Html.TextBoxFor(x => x.CallSection)
</div>
<div>
#Html.ValidationMessageFor(x => x.CallComments)<br />
#Html.LabelFor(x => x.CallComments)
#Html.TextAreaFor(x => x.CallComments)
</div>
<div>
#Html.ValidationMessageFor(x => x.CallKeywords)<br />
#Html.LabelFor(x => x.CallKeywords)
#Html.TextAreaFor(x => x.CallKeywords)
</div>
<div>
#Html.ValidationMessageFor(x=>x.Mp3)<br />
#Html.LabelFor(x=>x.Mp3)
#Html.TextBoxFor(x=>x.Mp3, new {type= "file"})
</div>
<div>
<input type="submit" value="Add MP3" />
</div>
}
any suggestions would be greatly appreciated.
TIA
doh! my bad. I wasn't using a form with multipart form data. when I added that, all is good in the world.

Partial View with Different Models, Null Reference Error

I am writing a registration page that has both options to register and log in. I would like for these views and models to remain separate, so I am using partial views. However, I am getting a null reference exception when the second partial view attempts to initialize its model. Help would be appreciated.
The null reference exceptions occurs at
#Html.Partial("Login",Model.Login)
RegisterModel
public class RegisterModel
{
public LoginModel Login { get; set; }
public RegisterModel()
{
Login = new LoginModel();
}
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
LoginModel
public class LoginModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
Login.cshtml
#model MvcApplication1.Models.LoginModel
#{
ViewBag.Title = "Log in";
}
<hgroup class="title">
<h1>#ViewBag.Title.</h1>
</hgroup>
<section id="loginForm">
#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>
}
</section>
#*<section class="social" id="socialLoginForm">
<h2>Use another service to log in.</h2>
#Html.Action("ExternalLoginsList", new { ReturnUrl = ViewBag.ReturnUrl })
</section>*#
And Register.cshtml (the index)
#model MvcApplication1.Models.RegisterModel
#{
ViewBag.Title = "stuff";
}
#section featured {
<section class="featured">
<div class="content-wrapper">
<div class="register">
<div class="registration_contents">
#Html.Partial("RegisterForm")
</div>
<div class="login_contents">
#Html.Partial("Login",Model.Login)
</div>
</div>
</div>
</section>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Good afternoon, it looks like in your get request for the Register view you are more than likely not directly instantiating your RegisterModel so when you pass Model.Login to your partial call it is null.

MVC Model not posting

When I click the login button I never get my model posted to the server. However if I accept a FormCollection I will see the values. How can I make this automatically bind to my model instead of searching the Form Collection?
From what I have read there are a few common problems for this:
1 - your view does not specify what model you are using (#model myApp.Models.name)
2 - Your model does not use properties
3 - Any of the required fields are missing
Controller
[HttpGet]
public ActionResult Password()
{
return View(new AuthViewModel());
}
[HttpPost]
public ActionResult Password(AuthViewModel password)
{
if (password == null || string.IsNullOrEmpty(password.Password))
{
ViewBag.Error = Constants.ErrorMessages.UserPassword_PassBlank;
return View(new AuthViewModel());
}
//success
return Redirect("/");
}
Model
public class AuthViewModel
{
public string Password { get; set; }
}
View
#model MvcApplication1.Models.AuthViewModel
#{
ViewBag.Title = "Password";
}
<h2>Password</h2>
#using (Html.BeginForm())
{
<div>#Html.TextBoxFor(m => m.Password,new{placeholder="Password",type="password",autofocus=""})</div>
<div><button id="btnLogin" type="submit">Login</button></div>
<div class="error">#ViewBag.Error</div>
}
Not sure why Dan's answer isn't working without trying it, looks like it should.
I took a look at some of my code for a login form, similar to yours.
Here's mine :
public class SignInModel
{
[Required]
[Display(Name = "Enter your email address")]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Enter your password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
The main difference I see is that mine has the [DataType(DataType.Password)] attribute on the password. Not sure if this makes that much difference though.
The other thing I noticed is different is that in my form I specify that the form method is POST. Also I've used the EditorFor() helper instead of textbox or password:
#using (Html.BeginForm("SignIn", "Account", "POST"))
{
<div class="form-field">
#Html.LabelFor(x => x.Email)
#Html.EditorFor(m => m.Email)
</div>
<div class="form-field">
#Html.LabelFor(x => x.Password)
#Html.EditorFor(m => m.Password)
</div>
<div class="form-remember">
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(x => x.RememberMe)
</div>
<button type="submit">
Sign In</button>
}
use the following
#using (Html.BeginForm())
{
<div>#Html.PasswordFor(model => model.Password)</div>
<div><input id="btnLogin" type="submit" value="Login"/></div>
<div class="error">#ViewBag.Error</div>
}

ASP.NET MVC3 Remote Validation does not output error message

I have a simple Model that is the default RegisterModel and upon creating a View based on that model for Create I end up having
public class RegisterModel
{
[Required]
[Remote("UserNameExists", "Account", "", ErrorMessage = "Username is already taken.")]
[Display(Name = "Username (spaces will be stripped, must be at least 6 characters long)")]
public string UserName { get; set; }
[Required]
[Editable(true)]
[Display(Name = "First and Last name")]
public string Name { get; set; }
[Required]
[DataType(DataType.EmailAddress, ErrorMessage = "You need to enter a valid email")]
[Remote("EmailExists", "Account", "", ErrorMessage = "Email is already taken.")]
[Display(Name = "Email address")]
public string Email { get; set; }
//[Required]
//[ValidatePasswordLength]
[DataType(DataType.Password)]
[Display(Name = "Create a password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Verify password")]
//[Compare("Password", ErrorMessage = "Password's do not match.")]
public string ConfirmPassword { get; set; }
}
and in the View:
<h3>
Details</h3>
#using (Html.BeginForm("GenerateBetaLink", "Account", FormMethod.Post, new { #id = "beta-user" }))
{
#Html.ValidationSummary(true)
<div>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
<p>
</p>
<p>
<input type="submit" value="Create Beta User" class="btn-submit" />
<span class="loading"></span>
</p>
</div>
}
My Validation Controller
public class ValidationController : Controller
{
public JsonResult UserNameExists(string UserName)
{
OnlineServicesRepository db = new OnlineServicesRepository();
var user = db.FindUserByUsername(UserName.Trim());
return user == null ?
Json(true, JsonRequestBehavior.AllowGet) :
Json(string.Format("{0} is not available.", UserName),
JsonRequestBehavior.AllowGet);
}
public JsonResult EmailExists(string Email)
{
OnlineServicesRepository db = new OnlineServicesRepository();
var user = db.FindUserByEmail(Email.Trim());
return user != null ?
Json(true, JsonRequestBehavior.AllowGet) :
Json(string.Format("{0} is not available.", Email),
JsonRequestBehavior.AllowGet);
}
}
My problem is that Remote Validation does fire, but does not write anything into the Error Message as it should, plus, the jQuery method .valid() keeps telling me that the form is valid:
(source: balexandre.com)
What am I missing here?
The MSDN article shows the same code (in the downloadable file)
The following worked fine for me:
Model:
public class RegisterModel
{
[Required]
[DataType(DataType.EmailAddress, ErrorMessage = "You need to enter a valid email")]
[Remote("EmailExists", "Home", "")]
[Display(Name = "Email address")]
public string Email { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(RegisterModel model)
{
return View(model);
}
public ActionResult EmailExists(string email)
{
if ((email ?? string.Empty).Contains("foo"))
{
return Json(email + " is not available", JsonRequestBehavior.AllowGet);
}
return Json(true, JsonRequestBehavior.AllowGet);
}
}
View:
#model RegisterModel
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.LabelFor(model => model.Email)
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
<input type="submit" value="OK" />
}
I had the same problem and resolved it by updating to the latest jQuery (1.6) and jQuery.validate (1.8) libraries. The easiest way to get these is searching NuGet for jQuery.

Resources