I'm trying to build the functionality where the user can upload a profile image to blob storage on registration and then i can be called and included in the manage index view.
I have been able to create the container but no blob is showing inside the container and the url is not being saved to the database either (just NULL) so I'm feeling like its the controller code that's the issue.
CONTROLLER
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model,
HttpPostedFileBase photo, PhotoService photoService)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, PhotoUrl = model.PhotoUrl };
ApplicationUser.PhotoUrl = await photoService.UploadPhotoAsync(photo);
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
// For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
//var db = new ApplicationDbContext();
//model.PhotoUrl = await photoService.UploadPhotoAsync(photo);
//db.Users.Add(user);
//db.SaveChanges();
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
MODEL
public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { 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 string PhotoUrl { get; set; }
}
SERVICE
public class PhotoService : IPhotoService
{
async public Task<string> UploadPhotoAsync(HttpPostedFileBase photoToUpload)
{
if (photoToUpload == null || photoToUpload.ContentLength == 0)
{
return null;
}
string fullPath = null;
Stopwatch timespan = Stopwatch.StartNew();
try
{
CloudStorageAccount storageAccount = StorageUtils.StorageAccount;
// Create the blob client and reference the container
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("images");
// Create a unique name for the images we are about to upload
string imageName = String.Format("task-photo-{0}{1}",
Guid.NewGuid().ToString(),
Path.GetExtension(photoToUpload.FileName));
// Upload image to Blob Storage
CloudBlockBlob blockBlob = container.GetBlockBlobReference(imageName);
blockBlob.Properties.ContentType = photoToUpload.ContentType;
await blockBlob.UploadFromStreamAsync(photoToUpload.InputStream);
fullPath = blockBlob.Uri.ToString();
timespan.Stop();
// log.TraceApi("Blob Service", "PhotoService.UploadPhoto", timespan.Elapsed, "imagepath={0}", fullPath);
}
catch (Exception ex)
{
// log.Error(ex, "Error upload photo blob to storage");
throw;
}
return fullPath;
}
I'm working on this with this tutorial as a guide - https://learn.microsoft.com/en-us/aspnet/aspnet/overview/developing-apps-with-windows-azure/building-real-world-cloud-apps-with-windows-azure/unstructured-blob-storage
Would really appreciate it if someone could let me know how to get this working.
Thanks
"an object reference is required for the nonstatic field method or property".
You need to init your user as follows:
var user = new ApplicationUser {
UserName = model.Email,
Email = model.Email,
PhotoUrl = await photoService.UploadPhotoAsync(photo)
};
var result = await UserManager.CreateAsync(user, model.Password);
Moreover, I found the tutorial defined a interface IPhotoService and implement it by the PhotoService class, but I found you define the PhotoService in your Register action. In order to leverage dependency injection, I would recommend you follow this tutorial.
Also, I would recommend you move the HttpPostedFileBase photo parameter from the Register method into your RegisterViewModel as follows:
public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { 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 Photo { get; set; }
}
Then, you could init your ApplicationUser instance as follows:
var user = new ApplicationUser {
UserName = model.Email,
Email = model.Email,
PhotoUrl = await photoService.UploadPhotoAsync(model.Photo)
};
And for your register view, you could init the input file as follows:
#using (Html.BeginForm("Register", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form", enctype = "multipart/form-data" }))
{
//other properties
#Html.TextBoxFor(m => m.Photo, new {type = "file"})
}
TEST:
In general, you need to make sure you have set the multipart/form-data for your register form in the register view. And the parameter photoService in the Register method is not null. Also, you need to make sure your container name images exists, or you need to await container.CreateIfNotExistsAsync(). Additionally, you could leverage debugging via Visual Studio and step into your code to narrow this issue, details about how to debug, you could follow here.
Related
hello Am trying to Upload an image to AspNetUser's table when a user register, i implemented everything and i can select an image but when i submit the form i get this Error:
System.FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
I don't know what's wrong. Please help.
this is my code
i added this new fields in the identitymodel
public string Region { get; set; }
public string UserBio { get; set; }
public byte[] ProfilePicture { get; set; }
public string WebUrl { get; set; }
public string CompanyName { get; set; }
RegisterViewModel
public class RegisterViewModel
{
[Display(Name = "Profile Picture")]
public byte[] ProfilePicture { get; set; }
[Display(Name = "Your Website Url")]
public string WebUrl { get; set; }
[Display(Name = "Your Company Name")]
public string CompanyName { get; set; }
}
ExtendedIdentityModels
public class ExtendedIdentityModels : RegisterViewModel
{
public HttpPostedFileBase UserProfilePicture { get; set; }
}
AcountController
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(ExtendedIdentityModels model)
{
if (ModelState.IsValid)
{
if (model.UserProfilePicture != null)
{
if (model.UserProfilePicture.ContentLength > (4 * 1024 * 1024))
{
ModelState.AddModelError("CustomError", "Image can not be lager than 4MB.");
return View();
}
if (!(model.UserProfilePicture.ContentType == "image/jpeg" || model.UserProfilePicture.ContentType == "image/gif"))
{
ModelState.AddModelError("CustomError", "Image must be in jpeg or gif format.");
}
}
byte[] data = new byte[model.UserProfilePicture.ContentLength];
model.UserProfilePicture.InputStream.Read(data, 0, model.UserProfilePicture.ContentLength);
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, FirstName = model.FirstName, LastName = model.Lastname, Gender = model.Gender, Country = model.Country, Region = model.Region, UserBio = model.UserBio, WebUrl = model.WebUrl, CompanyName = model.CompanyName, ProfilePicture = data };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
result = await UserManager.AddToRolesAsync(user.Id, model.RoleName);
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
// For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
return RedirectToAction("AccountEmailConfirmation", "Account");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Register.cshtml
<div class="form-group">
#Html.LabelFor(m => m.ProfilePicture, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.ProfilePicture, new { type = "file" })
#Html.ValidationMessage("CustomMessage", new { #class = "text-danger" })
</div>
</div>
I'm designing a register form and using ajax to send data to server.
My model:
public class RegisterViewModel
{
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[DataType(DataType.Password)]
[Compare("Password")]
public string ConfirmPassword { get; set; }
}
and the action:
[HttpPost]
[AllowAnonymous]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// do stuff...
}
}
}
My problem: result.Succeeded returned false with error message (Password is helloworld):
Passwords must have at least one non letter or digit character.
Passwords must have at least one digit ('0'-'9'). Passwords must have
at least one uppercase ('A'-'Z').
As you can see above, I didn't set any regular expression to Password and ConfirmPassword. So why was I getting that?
Looks like you're using Identity. In the IdentityConfig.cs, there's a section where you configure the requirements for password. Look for this section:
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
I'm trying to use a temporary password to register a user, which calls a password reset via email immediately during the registration process. When testing the code I get an error under the !ModelState.Valid check saying "The password field is required". I defined a password under model.Password for temporary use, so what am I missing here?
// POST: /Account/Register
[HttpPost]
[Authorize(Roles = "SuperAdmin, LEAAdmin, SchoolAdmin")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
model.BackgroundOnFile = false;
var userDetails = new ApplicationUser {
Id = model.Id,
UserName = model.Email,
Title = model.Title,
Email = model.Email,
FirstName = model.FirstName,
LastName = model.LastName,
LEAID = model.LEAID,
SchoolID = model.SchoolID,
BackgroundOnFile = model.BackgroundOnFile,
BoardStart = model.BoardStart,
BoardEnd = model.BoardEnd,
PhoneNumber = model.PhoneNumber,
IsBoardChair = model.IsBoardChair,
IsBoardMember = model.IsBoardMember,
IsAccountActive = model.IsAccountActive,
};
//Declaring in Scope
var result = new IdentityResult();
//Process if new
if(model.Id == "" || model.Id == "undefined" || model.Id == "null" || model.Id == "-9999")
{
model.Password = "D0neW!thTh15";
if (ModelState.IsValid)
{
result = await UserManager.CreateAsync(userDetails, model.Password);
var getPassword = new ForgotPasswordViewModel
{
Email = model.Email
};
//Email PW
await ForgotPassword(getPassword);
}
if(!ModelState.IsValid)
{
string validationErrors = string.Join(",",ModelState.Values.Where(E => E.Errors.Count > 0).SelectMany(E => E.Errors).Select(E => E.ErrorMessage).ToArray());
}
}
//Process if update
if (model.Id != "" || model.Id != "undefined" || model.Id != "null" || model.Id != "-9999")
{
if (ModelState.IsValid)
{
result = await UserManager.UpdateAsync(userDetails);
}
}
//Process Roles
//Remove access if deactivated
if (model.IsAccountActive == false)
{
var user = await UserManager.FindByIdAsync(userDetails.Id);
await UserManager.RemoveFromRolesAsync(userDetails.Id, UserManager.GetRoles(userDetails.Id).ToArray());
await UserManager.AddToRoleAsync(userDetails.Id, "Deactivated");
userDetails.LockoutEnabled = true;
await UserManager.UpdateAsync(userDetails);
}
else
{
//Moved to separate controller, for safety Admin rights can only be done for existing users
if(model.LEAAdmin == true)
{
await UserManager.AddToRoleAsync(userDetails.Id, "LEAAdmin");
}
//LEAAdmin is higher level, so don't allow override to a lower level
if(model.IsBoardChair == true && model.LEAAdmin == false)
{
await UserManager.AddToRoleAsync(userDetails.Id, "SchoolAdmin");
}
else
{
await UserManager.AddToRoleAsync(userDetails.Id, "User");
}
}
if (result.Succeeded)
{
//await SignInManager.SignInAsync(userDetails, isPersistent:false, rememberBrowser:false);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
//return RedirectToAction("Index", "Home");
return Json("Password sent!");
}
AddErrors(result);
// If we got this far, something failed, redisplay form
//return View(model);
return View();
}
Here's the code for ApplicationUser, in case you need it:
public class ApplicationUser : IdentityUser
{
//Extra items required to register
public string Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int LEAID { get; set; }
public int SchoolID { get; set; }
//public string Address1 { get; set; }
//public string Address2 { get; set; }
//public string City { get; set; }
//public string State { get; set; }
//Post Code is a string to accomodate future possible Canadian style post codes
//public string PostCode { get; set; }
public bool BackgroundOnFile { get; set; }
public bool IsBoardMember { get; set; }
public bool IsBoardChair { get; set; }
public System.DateTime? BoardStart { get; set; }
public System.DateTime? BoardEnd { get; set; }
public string NominatedBy { get; set; }
public bool IsAccountActive { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
UPDATE:
ViewModel as requested for Registration
[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")]
[System.Web.Mvc.CompareAttribute("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
ModelState validation happens in the default ModelBinder, when the values are passed to the server and the RegisterViewModel is created. What this means is that ModelState.IsValid does not automatically change when you update the model. To re-validate the model, you could do something like this:
//Set password here
ModelState.Clear(); // Clear the current ModelState
TryValidateModel(model); // Try to re-validate.
if (ModelState.IsValid) // Now check!
{
...
}
By doing this you are clearing the current ModelState and then forcing revalidation.
Hi guys so I'm using a custom membership provider and custom role provider. And it is logging in using these correctly. I also implemented my own Membership user object so that I can gain access to other user information, and I don't need to load all the data everytime I change page, but I currently cannot get this to work properly. Below is my user object:
public class User : MembershipUser
{
[Required(ErrorMessage = "Username cannot be blank")]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required(ErrorMessage = "Password cannot be blank")]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "User ID")]
public long UserID { get; set; }
[Display(Name = "Family Name")]
[StringLength(50, ErrorMessage = "Family name cannot be longer than 50 characters")]
public string FamilyName { get; set; }
[Display(Name = "Given Name")]
[StringLength(50, ErrorMessage = "Given name cannot be longer than 50 characters")]
public string GivenName { get; set; }
public Company Company { get; set; }
public virtual IIdentity Identity { get; set; }
}
And when the user logs in I call the following login method:
[AllowAnonymous]
[HttpPost]
public ActionResult Login(User model, string returnUrl)
{
FormsAuthentication.SignOut();
if(Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, true);
return RedirectToAction("Index", "");
}
ViewBag.Message = "Failed to login";
return View();
}
But when I call HttpContext.User in index it just contains name/ID, not the rest of my user object. Do I need to create a custom FormAuthentication object? Or is it standard process to store all this user information inside the HttpContext.Session object? Or get my user to extend the System.Security.Principle.IPrinciple? Or even in the Controller.TempData? Or somewhere else I'm not familiar with. I dont want to have to hit the database everytime to load the user data.
Sorry if these are obvious questions I'm fairly new to web development and not sure what the generic way of doing these things are. Trying to use the in build Authorize attributes.
I did it by implementing my own identity. That way it's easy to add as many properties as I need. Below is a code example with custom property friendlyName
public class Identity : IIdentity
{
public Identity(int id, string name, string friendlyName, string roles)
{
this.ID = id;
this.Name = name;
this.FriendlyName = friendlyName;
this.Roles = roles;
}
public Identity(string name, string data)
{
if (string.IsNullOrWhiteSpace(data))
throw new ArgumentException();
string[] values = data.Split('|');
if (values.Length != 3)
throw new ArgumentException();
this.Name = name;
this.ID = Convert.ToInt32(values[0]);
this.FriendlyName = values[1];
Roles = values[2];
}
public string AuthenticationType
{
get { return "Custom"; }
}
public bool IsAuthenticated
{
get { return true; }
}
public override string ToString()
{
return FriendlyName;
}
public string GetUserData()
{
return string.Format("{0}|{1}|{2}", ID, FriendlyName, Roles);
}
public int ID { get; private set; }
public string Name { get; private set; }
public string FriendlyName { get; private set; }
public string Roles { get; private set; }
}
//in controller on login action:
Identity id = new Identity(user.ID, user.Username, "some friendly name", user.Roles);
DateTime expire = DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(id.ID, user.Username, DateTime.Now, expire, false, id.GetUserData());
string hashTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hashTicket);
HttpContext.Response.Cookies.Add(cookie);
In global.asax you have:
public override void Init()
{
base.Init();
PostAuthenticateRequest += new EventHandler(MvcApplication_PostAuthenticateRequest);
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
if (authTicket == null || authTicket.Expired)
return;
Identity id = new Identity(authTicket.Name, authTicket.UserData);
Principal user = new Principal(id);
Context.User = user;
Thread.CurrentPrincipal = user;
}
}
I have a asp.net mvc application that uses standard account controller to handle auth.
The client asked me to add new fields as email, city, age to account creation.
Which would be the best way to extend account controller/model?
Should i modify aspnet_users table or should i create a new table with the new fields? Is it a right way to add new fields to AccountFormViewModel?
I guess you have two option:
1) Creating your custom membership provider.
2) Using the builtin profile membership provider
Anyway you have to implement/extend the AccountController and ViewModel that you get with a new MVC project.
If you choose the first option you can add your fields to the aspenet_users table.
If you choose the second option you use the profile table that the framework want you to create.
If you want a "super integration" of your fields you can consider to extend the IIdentity and IPrincipal interfaces. Look at this answer for some more info.
Hope it helps
First you have modify the RegisterModel AcountModels, and add new fileds
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 string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
in AcountController you modify the Register action
[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);
using (var context = new ECDB())
{
var username = model.UserName;
var user = context.UserProfiles.SingleOrDefault(u => u.UserName == username);
user.FirstName = model.FirstName;
user.LastName = model.LastName;
user.Email = model.Email;
context.SaveChanges();
}
return RedirectToAction("Index", "Home");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}