public void ConfigureServices(IServiceCollection services) { services.Configure<PasswordHasherOptions>(options => options.IterationCount = 100_000);
Learn Blazor WebAssembly and Web API on .NET 6 by building a shopping cart application using C#. This course also provides a guide on how to integrate a payment gateway into your Blazor WebAssembly component, so that a user is able to pay for products through your application using a debit or credit card or PayPal account.
⭐️ Course Contents ⭐️
⌨️ (0:00:00) Introduction
⌨️ (0:00:51) Create the Database using EF Core Code First Database Migrations
⌨️ (0:26:05) Retrieve Product Data from Database (Web API component)
⌨️ (0:30:17) Create Classes for Data Transfer Objects (DTOs)
⌨️ (0:36:22) Create ProductRepository Class (Repository Design Pattern)
⌨️ (0:43:05) Create ProductController Class
⌨️ (0:51:08) Create DtoConversion Class (DTO Conversion Extension methods)
⌨️ (0:57:45) Display Product Data to User (Blazor WebAssembly Component)
⌨️ (1:39:59) Display Data for Specific Product to User (Web API and Blazor)
⌨️ (2:06:07) Add Product to Shopping Cart (Web API and Blazor)
⌨️ (2:52:40) Remove Product from Shopping Cart (Web API and Blazor)
⌨️ (3:14:03) Update the Quantity of Products in the Shopping Cart (Web API, Blazor, Blazor JavaScript Interoperability)
⌨️ (3:44:01) Update the Header Menu in Response to a Change to the State of the Shopping Cart (Creating Custom Events in Blazor)
⌨️ (4:04:48) Integration of PayPal Payment Gateway into Blazor Component
⌨️ (4:36:03) Dynamically Populate the Side-Bar Menu (Web API and Blazor)
⌨️ (5:05:44) Optimise Code for Performance (Web API and Blazor)
⌨️ (5:08:26) Use Include Extension Method in LINQ Query (Web API)
⌨️ (5:14:00) User Local Storage Functionality (Blazor)
⌨️ (5:35:42) Outro
- Detect unknown component parameters
- Properties marked with [SupplyParameterFromQuery] attribute should also be marked as [Parameter]
- Properties marked with [EditorRequired] attribute should also be marked as [Parameter]
- Methods marked with [JSInvokable] attribute must be public
- JSRuntime must not be used in OnInitialized or OnInitializedAsync
- Use InvokeVoidAsync instead of InvokeAsync when the return value is not used
- Do not overwrite parameter values
مراحل فعال سازی آپلود فایلها در ASP.NET Core
مرحلهی اول فعال سازی آپلود فایلها در ASP.NET Core، شامل افزودن ویژگی "enctype="multipart/form-data به یک فرم تعریف شدهاست:
<form method="post" asp-action="Index" asp-controller="TestFileUpload" enctype="multipart/form-data"> <input type="file" name="files" multiple /> <input type="submit" value="Upload" /> </form>
در سمت سرور، امضای اکشن متد دریافت کنندهی این فایلها به صورت ذیل خواهد بود:
[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Index(IList<IFormFile> files)
یافتن جایگزینی برای Server.MapPath در ASP.NET Core
زمانیکه فایل ارسالی، در سمت سرور دریافت شد، مرحلهی بعد، ذخیره سازی آن بر روی سرور است و از آنجائیکه ما دقیقا نمیدانیم ریشهی سایت در کدام پوشهی سرور واقع شدهاست، میشد از متد Server.MapPath برای یافتن دقیق آن کمک گرفت. با حذف این متد در ASP.NET Core، روش یافتن ریشهی سایت یا همان پوشهی wwwroot در اینجا شامل مراحل ذیل است:
public class TestFileUploadController : Controller { private readonly IHostingEnvironment _environment; public TestFileUploadController(IHostingEnvironment environment) { _environment = environment; }
پس از آن خاصیت environment.WebRootPath_ به ریشهی پوشهی wwwroot برنامه، بر روی سرور اشاره میکند. به این ترتیب میتوان مسیر دقیقی را جهت ذخیره سازی فایلهای رسیده، مشخص کرد.
امکان ذخیره سازی async فایلها در ASP.NET Core
عملیات کار با فایلها، عملیاتی است که از مرزهای IO سیستم عبور میکند. به همین جهت یکی از بهترین مثالهای پیاده سازی async، جهت رها سازی تردهای برنامه و بالا بردن میزان پاسخدهی آن با بالا بردن تعداد تردهای آزاد بیشتر است. در ASP.NET Core، نوشتن async محتوای فایل رسیده در یک stream پشتیبانی میشود و این stream میتواند یک FileStream و یا MemoryStream باشد. در ذیل نحوهی کار async با یک FileStream را مشاهده میکنید:
[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Index(IList<IFormFile> files) { var uploadsRootFolder = Path.Combine(_environment.WebRootPath, "uploads"); if (!Directory.Exists(uploadsRootFolder)) { Directory.CreateDirectory(uploadsRootFolder); } foreach (var file in files) { if (file == null || file.Length == 0) { continue; } var filePath = Path.Combine(uploadsRootFolder, file.FileName); using (var fileStream = new FileStream(filePath, FileMode.Create)) { await file.CopyToAsync(fileStream).ConfigureAwait(false); } } return View(); }
چون برنامههای ASP.NET Core قابلیت اجرای بر روی لینوکس را نیز دارند، تا حد امکان باید از Path.Combine جهت جمع زدن اجزای مختلف یک میسر، استفاده کرد. از این جهت که در لینوکس، جداکنندهی اجزای مسیرها، / است بجای \ در ویندوز و متد Path.Combine به صورت خودکار این مسایل را لحاظ خواهد کرد.
در آخر با استفاده از متد file.CopyToAsync کار نوشتن غیرهمزمان محتوای فایل دریافتی در یک FileStream انجام میشود؛ به همین جهت در امضای متد فوق، <async Task<IActionResult را نیز ملاحظه میکنید.
پشتیبانی کامل از Model Binding آپلود فایلها در ASP.NET Core
در ASP.NET MVC 5.x اگر ویژگی Required را بر روی یک خاصیت از نوع HttpPostedFileBase قرار دهید ... کار نمیکند و در سمت کلاینت تاثیری را به همراه نخواهد داشت؛ مگر اینکه تنظیمات سمت کلاینت آنرا به صورت دستی انجام دهیم. این مشکلات در ASP.NET Core، کاملا برطرف شدهاند:
public class UserViewModel { [Required(ErrorMessage = "Please select a file.")] [DataType(DataType.Upload)] public IFormFile Photo { get; set; } }
@model UserViewModel <form method="post" asp-action="UploadPhoto" asp-controller="TestFileUpload" enctype="multipart/form-data"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <input asp-for="Photo" /> <span asp-validation-for="Photo" class="text-danger"></span> <input type="submit" value="Upload"/> </form>
اینبار جهت فعال سازی و استفادهی از قابلیتهای Model Binding میتوان از ModelState نیز بهره گرفت:
[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> UploadPhoto(UserViewModel userViewModel) { if (ModelState.IsValid) { var formFile = userViewModel.Photo; if (formFile == null || formFile.Length == 0) { ModelState.AddModelError("", "Uploaded file is empty or null."); return View(viewName: "Index"); } var uploadsRootFolder = Path.Combine(_environment.WebRootPath, "uploads"); if (!Directory.Exists(uploadsRootFolder)) { Directory.CreateDirectory(uploadsRootFolder); } var filePath = Path.Combine(uploadsRootFolder, formFile.FileName); using (var fileStream = new FileStream(filePath, FileMode.Create)) { await formFile.CopyToAsync(fileStream).ConfigureAwait(false); } RedirectToAction("Index"); } return View(viewName: "Index"); }
بررسی پسوند فایلهای رسیدهی به سرور
ASP.NET Core دارای ویژگی است به نام FileExtensions که ... هیچ ارتباطی به خاصیتهایی از نوع IFormFile ندارد:
[FileExtensions(Extensions = ".png,.jpg,.jpeg,.gif", ErrorMessage = "Please upload an image file.")]
در ادامه جهت بررسی پسوندهای فایلهای رسیده، میتوان یک ویژگی اعتبارسنجی سمت سرور جدید را طراحی کرد:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class UploadFileExtensionsAttribute : ValidationAttribute { private readonly IList<string> _allowedExtensions; public UploadFileExtensionsAttribute(string fileExtensions) { _allowedExtensions = fileExtensions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); } public override bool IsValid(object value) { var file = value as IFormFile; if (file != null) { return isValidFile(file); } var files = value as IList<IFormFile>; if (files == null) { return false; } foreach (var postedFile in files) { if (!isValidFile(postedFile)) return false; } return true; } private bool isValidFile(IFormFile file) { if (file == null || file.Length == 0) { return false; } var fileExtension = Path.GetExtension(file.FileName); return !string.IsNullOrWhiteSpace(fileExtension) && _allowedExtensions.Any(ext => fileExtension.Equals(ext, StringComparison.OrdinalIgnoreCase)); } }
public class UserViewModel { [Required(ErrorMessage = "Please select a file.")] //`FileExtensions` needs to be applied to a string property. It doesn't work on IFormFile properties, and definitely not on IEnumerable<IFormFile> properties. //[FileExtensions(Extensions = ".png,.jpg,.jpeg,.gif", ErrorMessage = "Please upload an image file.")] [UploadFileExtensions(".png,.jpg,.jpeg,.gif", ErrorMessage = "Please upload an image file.")] [DataType(DataType.Upload)] public IFormFile Photo { get; set; } }
An exception of type 'System.Data.SqlClient.SqlException' occurred in EntityFramework.SqlServer.dll but was not handled in user code Additional information: CREATE FULLTEXT CATALOG statement cannot be used inside a user transaction.