فعال سازی عملیات CRUD در Kendo UI Grid
ASP.NET MVC #9
https://www.dntips.ir/Post/812/asp-net-mvc-9
namespace Project.Models { public class EmployeeCreateModel { [Required] [Display(Name = "آدرس ایمیل")] [EmailAddress(ErrorMessage = "لطفاً {0} معتبر وارد کنید.")] [Remote("UserExistByEmailValidation", HttpMethod = "POST", ErrorMessage = "ایمیل وارد شده هم اکنون توسط یکی از کاربران مورد استفاده است.")] public string Email { get; set; } ... } }
namespace Project.Models { public class EmployeeEditModel { public int Id { get; set; } [Required] [Display(Name = "آدرس ایمیل")] [EmailAddress(ErrorMessage = "لطفاً {0} معتبر وارد کنید.")] [Remote("EmailExistForOtherUserValidation", AdditionalFields = "Id", HttpMethod = "POST", ErrorMessage = "ایمیل وارد شده هم اکنون توسط یکی از کاربران مورد استفاده است.")] public string Email { get; set; } .... } }
namespace Project.Web.Controllers { [RoutePrefix("UserValidation")] [Route("{Action}")] [OutputCache(Location = OutputCacheLocation.None, NoStore = true)] public partial class UserValidationController : Controller { readonly IUserService<User> _userService; readonly IUnitOfWork _uow; public UserValidationController(IUnitOfWork uow, IUserService<User> userService) { _userService = userService; _uow = uow; } [HttpPost] [Route("~/CheckEmail", Name = "UserExistByEmailValidation")] public virtual JsonResult CheckEmail(string email) { return Json(!_userService.UserExistsByEmail(email)); } [HttpPost] [Route("~/CheckEmailForOtherUser", Name = "EmailExistForOtherUserValidation")] public virtual JsonResult CheckEmailForOtherUser(string email, int id) { return Json(!_userService.EmailExistForOtherUser(email, id)); } } }
@section Scripts { @Scripts.Render("~/bundles/jqueryval") }
Services.AddScoped<SomeCustomType>(); [Route("[controller]")] [ApiController] public class MyController : ControllerBase { // Binding from the services [HttpPost] public ActionResult Post(SomeCustomType service) => Ok(); }
services.Configure<ApiBehaviorOptions>(options => { options.DisableImplicitFromServicesParameters = true; });
یک نکتهی تکمیلی: روش لغو صف درخواستهای مکرر fetch ارسالی به سمت سرور
ورودی جستجوی بالای صفحه را درنظر بگیرید که بهازای هربار ورود حرفی، یک درخواست fetch جدید را به سمت سرور ارسال میکند تا نتایج جستجوی حاصل را دریافت کند. مشکل اینجاست که ما تنها به آخرین درخواست fetch ارسال شدهی به سمت سرور نیاز داریم و نه به تمام درخواستهای دیگری که صادر شدهاند. به همین جهت این صف درخواستهای fetch قبلی، غیربهینه بوده و ترافیک بالایی را سبب میشوند. یک روش مواجه شدن با این مساله، استفاده از مفهومی به نام debounce است که در پشت صحنه، از یک تایمر استفاده میکند و فقط هر چند ثانیه یکبار، یک درخواست جدید را به همراه آخرین متن ورودی، به سمت سرور ارسال خواهد کرد. راه دیگری هم برای مواجه شدن با این مشکل، در مرورگرهای جدید پیشبینی شدهاست که AbortController نام دارد. با استفاده از آن میتوان «سیگنالی» را به صف درخواستهای پرتعداد fetch قبلی حاصل از ورود اطلاعات کاربر ارسال کنیم که ... «لغو شوید» و به سمت سرور ارسال نشوید.
برای توضیح بهتر آن، به مثال زیر دقت کنید:
<!DOCTYPE html> <html> <body> <input id="search" type="number" /> <script> const results = []; const search = document.getElementById("search"); let controller = new AbortController(); let signal = controller.signal; const onChange = () => { const value = search.value; if (value) { controller.abort(); controller = new AbortController(); signal = controller.signal; getPost(value, signal); } }; search.onkeyup = onChange; </script> </body> </html>
- در اینجا یک input box را داریم که ابتدا، یافت شده و سپس به رخداد onkeyup آن، متد onChange نسبت داده شدهاست تا هربار که کاربر، اطلاعاتی را وارد میکند، فراخوانی شود.
- در ابتدای اسکریپت هم نحوهی نمونه سازی شیء استاندارد جاوااسکریپتی AbortController و دسترسی به شیء signal آنرا مشاهده میکنید.
- در متد onChange، ابتدا مقدار جدید ورودی کاربر، دریافت میشود، سپس این AbortController، لغو میشود و بعد یک نمونهی جدید از آن ایجاد شده و مجددا به شیء signal آن دسترسی پیدا میکنیم تا آنرا به متد getPost ارسال کنیم. این متد هم چنین پیاده سازی را دارد:
const getPost = (value, signal) => { fetch( `https://site.com/search/${value}`, { signal } ); };
همانطور که مشاهده میکنید، تابع fetch، قابلیت پذیرش شیء signal را هم دارد. زمانیکه با هربار تایپ کاربر، متد ()controller.abort فراخوانی میشود، سیگنالی را به fetch «قبلی» متصل به آن ارسال میکند که ... دیگر به سمت سرور ارسال نشو و متوقف شو. با اینکار فقط آخرین ورودی کاربر، سبب بروز یک fetch موفق میشود و ترافیک ارسالی به سمت سرور کاهش پیدا میکند (چون تمام fetchهای قبلی، سیگنال abort را دریافت کردهاند)؛ مانند مثال زیر که کاربر، 5 بار حروفی را وارد کرده و به ازای هربار ورود حرفی، یک درخواست fetch جدید، ایجاد شده، اما ... فقط آخرین درخواست ارسالی او موفق بوده و نتیجهای را بازگشت داده و مابقی درخواستها ... abort شدهاند. این عملیات abort، در سمت کاربر اعمال میشود؛ یعنی اصلا درخواستی به سمت سرور ارسال نمیشود و این لغو درخواست، توسط برنامهی سمت سرور انجام نشدهاست.
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
[HttpPost] public ActionResult EditProductData(string oper, string title,string groupTitle,string shopTitle,int AvailableCount,int Priority, int? id) { if (oper == "add") { var product = new Product() { Title = title, }; db.Products.Add(product); db.SaveChanges(); return Content("true"); } else if (oper == "del") { var product = db.Products.Find(id); db.Products.Remove(product); return Content("true"); } else if (oper == "edit") { var product = db.Products.Find(id); product.Title = title; product.GroupId = int.Parse(groupTitle); product.ShopId = int.Parse(shopTitle); product.AvailableCount = AvailableCount; product.Priority = Priority; db.SaveChanges(); return Content("true"); } return Content("false"); }
پیاده سازی AOP در TypeScript
I’ve been kicking around the idea of combining [HttpPost] and [ValidateAntiForgeryToken] in an application using authentication cookies. Both attributes typically appear together to prevent cross-site request forgeries in MVC applications using cookie based authentication. The result looks like the following.