Chuyển đến nội dung chính

ASP.NET Identity: Custom Identity User and thêm trang Profile - Part 4

Trong bài viết này, chúng ta sẽ thêm một số thuộc tính cho Identity User và tạo trang Profile dùng để update thông tin cho user

Xem thêm:

Custom Identity User

Mở file ApplicationUser.cs, thêm 2 thuộc tính DateOfBirth và ProfilePicture

Chúng ta định nghĩa Profile Picture kiểu byte[] dùng để lưu trữ image data. Nếu các bạn muốn lưu trữ bằng file vật lý trong thư mục wwwroot thì tham khảo thêm ở đây: ASP.NET Core: Upload file

public class ApplicationUser : IdentityUser
{
	public string FirstName { get; set; }
	public string LastName { get; set; }
	public DateTime? DateOfBirth { get; set; }
	public byte[] ProfilePicture { get; set; }
}
Mở Package Manager Console, thực hiện tạo mới 1 migration
Add-Migration AddProfilePicture
Update-Database

Thêm trang Profile

Chúng ta sẽ có các step như sau
  1. Thêm 2 action: Manage (get) và Manage (post) để quản lý việc hiển thị và update Identity User
  2.  Thêm Model UserProfileViewModel chứa các properties dùng để hiển thị trên trang Profile
  3. Thêm option SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true để hệ thống không thêm thêm Required cho các property
Thêm file UserProfileViewModel
public class UserProfileViewModel
{
	[Display(Name = "First Name")]
	[Required(ErrorMessage = "Your First Name is required")]
	public string FirstName { get; set; }

	[Display(Name = "Last Name")]
	[Required(ErrorMessage = "Your Last Name is required")]
	public string LastName { get; set; }

	[Display(Name = "User name")]
	public string Username { get; set; }
	[Display(Name = "Date Of Birth")]
	[BindProperty, DataType(DataType.Date)]
	public DateTime? DateOfBirth { get; set; }

	[Phone]
	[Display(Name = "Phone number")]
	[Required]
	public string PhoneNumber { get; set; }
	[Display(Name = "Profile Picture")]
	public byte[] ProfilePicture { get; set; }
}
Mở file AccountController, thêm đoạn code như sau:
public async Task<IActionResult> Manage()
{
	var username = _userManager.GetUserName(User);
	if(string.IsNullOrEmpty(username))
	{
		return RedirectToAction("Login", "Account");
	}
	var user = await _userManager.FindByNameAsync(username);
	if (user == null)
	{
		return RedirectToAction("Login", "Account");
	}
	var userProfile = new UserProfileViewModel
	{
		PhoneNumber = user.PhoneNumber??string.Empty,
		Username = user.UserName ?? string.Empty,
		FirstName = user.FirstName,
		LastName = user.LastName,
		DateOfBirth = user.DateOfBirth,
		ProfilePicture = user.ProfilePicture
	};
	return View(userProfile);
}

[HttpPost]
public async Task<IActionResult> Manage(UserProfileViewModel model, IFormFile ProfilePicture)
{
	if(!ModelState.IsValid)
	{
		model.Username = User.Identity.Name;
		return View(model);
	}
	var username = User.Identity?.Name;
	if (string.IsNullOrEmpty(username))
	{
		return RedirectToAction("Login", "Account");
	}
	var user = await _userManager.FindByNameAsync(username);
	if (user == null)
	{
		return RedirectToAction("Login", "Account");
	}
	user.FirstName = model.FirstName;
	user.LastName = model.LastName;
	user.PhoneNumber = model.PhoneNumber;
	user.DateOfBirth = model.DateOfBirth;
	if(ProfilePicture != null)
	{
		using (var dataStream = new MemoryStream())
		{
			await ProfilePicture.CopyToAsync(dataStream);
			user.ProfilePicture = dataStream.ToArray();
		}
	}
	await _userManager.UpdateAsync(user);
	model.ProfilePicture = user.ProfilePicture;
	model.Username = username;
	return View(model);
}
Thêm View cho action Manage:
@model UserProfileViewModel

<form id="profile-form" method="post" enctype="multipart/form-data">
    @Html.ValidationSummary(true)
    <div class="row">
        <div class="col-md-6">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group mb-3">
                <label asp-for="FirstName"></label>
                <input asp-for="FirstName" class="form-control" />
                <span asp-validation-for="FirstName" class="text-danger"></span>
            </div>
            <div class="form-group mb-3">
                <label asp-for="LastName"></label>
                <input asp-for="LastName" class="form-control" />
                <span asp-validation-for="LastName" class="text-danger"></span>
            </div>
            <div class="form-group mb-3">
                <label asp-for="Username"></label>
                <input asp-for="Username" class="form-control" disabled />
                <span asp-validation-for="Username" class="text-danger"></span>
            </div>
            <div class="form-group mb-3">
                <label asp-for="DateOfBirth"></label>
                <input asp-for="DateOfBirth" class="form-control" max="@DateTime.Now.ToString("yyyy-MM-ddThh:mm")" min="@DateTime.Now.AddYears(-100).ToString("yyyy-MM-ddThh:mm")" />
                <span asp-validation-for="DateOfBirth" class="text-danger"></span>
            </div>
            <div class="form-group mb-3">
                <label asp-for="PhoneNumber"></label>
                <input asp-for="PhoneNumber" class="form-control" />
                <span asp-validation-for="PhoneNumber" class="text-danger"></span>
            </div>
            <button id="update-profile-button" type="submit" class="btn btn-primary">Save</button>
        </div>
        <div class="col-md-6">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group mb-3">
                <label asp-for="ProfilePicture" style="width: 100%;"></label>
                @if (Model.ProfilePicture != null && Model.ProfilePicture.Length > 0)
                {
                    <img id="profilePicture" style="width:275px;height:275px; object-fit:cover" src="data:image/*;base64,@(Convert.ToBase64String(Model.ProfilePicture))">
                }
                else
                {
                    <img id="profilePicture" style="width:275px;height:275px; object-fit:cover" src="~/images/default_avatar-png.png">
                }
            </div>
            <div class="form-group mb-3">
                <input type="file" class="form-control"
                       accept=".png,.jpg,.jpeg,.gif,.tif"
                       asp-for="ProfilePicture"
                       onchange="document.getElementById('profilePicture').src = window.URL.createObjectURL(this.files[0])" />
                <span asp-validation-for="ProfilePicture" class="text-danger"></span>
            </div>
        </div>

    </div>
</form>

 

Set SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true trong Program.cs để hệ thống không thêm attribute Required các các thuộc tính non-null trên View

// Add services to the container.
builder.Services.AddControllersWithViews(options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

var app = builder.Build();
Nguyên nhân:
The validation system in .NET Core 3.0 and later treats non-nullable parameters or bound properties as if they had a [Required] attribute.

Build và run application để kiểm tra thành quả.

Download source code: https://github.com/anbinhtrong/AuthenticationAndAuthorization/releases/tag/profile_page_v4.0

Tham khảo

https://codewithmukesh.com/blog/user-management-in-aspnet-core-mvc/#Adding_a_Profile_Picture 

Chúc các bạn thành công!

Nhận xét

Bài đăng phổ biến từ blog này

[ASP.NET MVC] Authentication và Authorize

Một trong những vấn đề bảo mật cơ bản nhất là đảm bảo những người dùng hợp lệ truy cập vào hệ thống. ASP.NET đưa ra 2 khái niệm: Authentication và Authorize Authentication xác nhận bạn là ai. Ví dụ: Bạn có thể đăng nhập vào hệ thống bằng username và password hoặc bằng ssh. Authorization xác nhận những gì bạn có thể làm. Ví dụ: Bạn được phép truy cập vào website, đăng thông tin lên diễn đàn nhưng bạn không được phép truy cập vào trang mod và admin.

ASP.NET MVC: Cơ bản về Validation

Validation (chứng thực) là một tính năng quan trọng trong ASP.NET MVC và được phát triển trong một thời gian dài. Validation vắng mặt trong phiên bản đầu tiên của asp.net mvc và thật khó để tích hợp 1 framework validation của một bên thứ 3 vì không có khả năng mở rộng. ASP.NET MVC2 đã hỗ trợ framework validation do Microsoft phát triển, tên là Data Annotations. Và trong phiên bản 3, framework validation đã hỗ trợ tốt hơn việc xác thực phía máy khách, và đây là một xu hướng của việc phát triển ứng dụng web ngày nay.

Tổng hợp một số kiến thức lập trình về Amibroker

Giới thiệu về Amibroker Amibroker theo developer Tomasz Janeczko được xây dựng dựa trên ngôn ngữ C. Vì vậy bộ code Amibroker Formula Language sử dụng có syntax khá tương đồng với C, ví dụ như câu lệnh #include để import hay cách gói các object, hàm trong các block {} và kết thúc câu lệnh bằng dấu “;”. AFL trong Amibroker là ngôn ngữ xử lý mảng (an array processing language). Nó hoạt động dựa trên các mảng (các dòng/vector) số liệu, khá giống với cách hoạt động của spreadsheet trên excel.