http://www.microsoft.com/bizspark/
هزینه استفاده از دات نت فریم ورک چقدر است؟
http://www.microsoft.com/bizspark/
الگوهای طراحی، سندها و راه حلهای از پیش تعریف شده و تست شدهای برای مسائل و مشکلات روزمرهی برنامه نویسی میباشند که هر روزه ما را درگیر خودشان میکنند. هر چقدر مقیاس پروژه وسیعتر و تعداد کلاسها و اشیاء بزرگتر باشند، درگیری برنامه نویس و چالش برای مرتب سازی و خوانایی برنامه و همچنین بالا بردن کارآیی و امنیت افزونتر میشود. از همین رو استفاده از ساختارهایی تست شده برای سناریوهای یکسان، امری واجب تلقی میشود.
الگوهای طراحی از لحاظ سناریو، به سه گروه عمده تقسیم میشوند:
1- تکوینی: هر چقدر تعداد کلاسها در یک پروژه زیاد شود، به مراتب تعداد اشیاء ساخته شده از آن نیز افزوده شده و پیچیدگی و درگیری نیز افزایش مییابد. راه حلهایی از این دست، تمرکز بر روی مرکزیت دادن به کلاسها با استفاده از رابطها و کپسوله نمودن (پنهان سازی) اشیاء دارد.
2- ساختاری: گاهی در پروژهها پیش میآید که میخواهیم ارتباط بین دو کلاس را تغییر دهیم. از این رو امکان از هم پاشی اجزایِ دیگر پروژه پیش میآید. راه حلهای ساختاری، سعی در حفظ انسجام پروژه در برابر این دست از تغییرات را دارند.
3- رفتاری: گاهی بنا به مصلحت و نیاز مشتری، رفتار یک کلاس میبایستی تغییر نماید. مثلا چنانچه کلاسی برای ارائه صورتحساب داریم و در آن میزان مالیات 30% لحاظ شده است، حال این درصد باید به عددی دیگر تغییر کند و یا پایگاه داده به جای مشاهدهی تعدادِ معدودی گره از درخت، حال میبایست تمام گرهها را ارائه نماید.
الگوی فکتوری:
الگوی فکتوری در دستهء اول قرار میگیرد. من در اینجا به نمونهای از مشکلاتی که این الگو حل مینماید، اشاره میکنم:
فرض کنید یک شرکت بزرگ قصد دارد تا جزییات کامل خرید هر مشتری را با زدن دکمه چاپ ارسال نماید. چنین شرکت بزرگی بر اساس سیاستهای داخلی، بر حسب میزان خرید، مشتریان را به چند گروه مشتری معمولی و مشتری ممتاز تقسیم مینماید. در نتیجه نمایش جزییات برای آنها با احتساب میزان تخفیف و به عنوان مثال تعداد فیلدهایی که برای آنها در نظر گرفته شده است، تفاوت دارد. بنابراین برای هر نوع مشتری یک کلاس وجود دارد.
یک راه این است که با کلیک روی دکمهی چاپ، نوع مشتری تشخیص داده شود و
به ازای نوع مشتری، یک شیء از کلاس مشخص شده برای همان نوع ساخته شود.
// Get Customer Type from Customer click on Print Button int customerType = 0; // Create Object without instantiation object obj; //Instantiate obj according to customer Type if (customerType == 1) { obj = new Customer1(); } else if (customerType == 2) { obj = new Customer2(); } // Problem: // 1: Scattered New Keywords // 2: Client side is aware of Customer Type
همانگونه که مشاهده مینمایید در این سبک کدنویسی غیرحرفهای، مشکلاتی مشهود است که قابل اغماض نیستند. در ابتدا سمت کلاینت دسترسی مستقیم به کلاسها دارد و همانگونه که در شکل بالا قابل مشاهده است کلاینت مستقیما به کلاس وصل است. مشکل دوم عدم پنهان سازی کلاس از دید مشتری است.
راه حل: این مشکل با استفاده از الگوی فکتوری قابل حل است. با استناد به الگوی فکتوری، کلاینت تنها به کلاس فکتوری و یک اینترفیس دسترسی دارد و کلاسهای فکتوری و اینترفیس، حق دسترسی به کلاسهای اصلی برنامه را دارند.
گام نخست: در ابتدا یک class library به نام Interface ساخته و در آن یک کلاس با نام ICustomer می سازیم که متد Report() را معرفی مینماید.
//Interface
namespace Interface { public interface ICustomer { void Report(); } }
گام دوم: یک class library به نام MainClass ساخته و با Add Reference کلاس Interface را اضافه نموده، در آن دو کلاس با نام Customer1, Customer2 میسازیم و using Interface را Import مینماییم. هر دو کلاس از ICustomer ارث میبرند و سپس متد Report() را در هر دو کلاس Implement مینماییم.
// Customer1 using System; using Interface; namespace MainClass { public class Customer1 : ICustomer { public void Report() { Console.WriteLine("این گزارش مخصوص مشتری نوع اول است"); } } } //Customer2 using System; using Interface; namespace MainClass { public class Customer2 : ICustomer { public void Report() { Console.WriteLine("این گزارش مخصوص مشتری نوع دوم است"); } } }
گام سوم: یک class library به نام FactoryClass ساخته و با Add Reference کلاس Interface, MainClass را اضافه نموده، در آن یک کلاس با نام clsFactory می سازیم و using Interface, using MainClass را Import مینماییم. پس از آن یک متد با نام getCustomerType ساخته که ورودی آن نوع مشتری از نوع int است و خروجی آن از نوع Interface-ICustomer و بر اساس کد نوع مشتری object را از کلاس Customer1 و یا Customer2 میسازیم و آن را return می نماییم.
//Factory using System; using Interface; using MainClass; namespace FactoryClass { public class clsFactory { static public ICustomer getCustomerType(int intCustomerType) { ICustomer objCust; if (intCustomerType == 1) { objCust = new Customer1(); } else if (intCustomerType == 2) { objCust = new Customer2(); } else { return null; } return objCust; } } }
گام چهارم (آخر): در قسمت UI Client، کد نوع مشتری را از کاربر دریافت کرده و با Add Reference کلاس Interface, FactoryClass را اضافه نموده (دقت نمایید هیچ دسترسی به کلاسهای اصلی وجود ندارد)، و using Interface, using FactoryClass را Import مینماییم. از clsFactory تابع getCustomerType را فراخوانی نموده (به آن کد نوع مشتری را پاس میدهیم) و خروجی آن را که از نوع اینترفیس است به یک object از نوع ICustomer نسبت میدهیم. سپس از این object متد Report را فراخوانی مینماییم. همانطور که از شکل و کدها مشخص است، هیچ رابطه ای بین UI(Client) و کلاسهای اصلی برقرار نیست.
//UI (Client) using System; using FactoryClass; using Interface; namespace DesignPattern { class Program { static void Main(string[] args) { int intCustomerType = 0; ICustomer objCust; Console.WriteLine("نوع مشتری را وارد نمایید"); intCustomerType = Convert.ToInt16(Console.ReadLine()); objCust = clsFactory.getCustomerType(intCustomerType); objCust.Report(); Console.ReadLine(); } } }
<link rel="stylesheet" href="@Url.Content("~/Content/bootstrap-rtl.css")" type="text/css" /> <script type="text/javascript" src="@Url.Content("~/scripts/jquery-2.0.2.min.js")"></script> <script type="text/javascript" src="@Url.Content("~/scripts/jquery-ui-1.10.3.min.js")"></script> <script type="text/javascript" src="@Url.Content("~/scripts/bootstrap-rtl.js")"></script> <script type="text/javascript" src="@Url.Content("~/scripts/searchboxmvc.js")"></script>
[HttpPost] public virtual ActionResult LoadData(string fieldName, string value, string stringFilterMode = "startWith") { Thread.Sleep(2000); var models = MakePersons(); if (fieldName == "Id") { models = models.Where(p => p.Id == int.Parse(value)).Take(1).ToList(); } else if (fieldName == "FirstName") { models = models.Where(p => p.FirstName.StartsWith(value)).ToList(); } return Json(new { Status = "OK", Records = models }); } private List<Person> MakePersons() { var lst = new List<Person>(); lst.Add(new Person() { Id = 1, Code = "Uytffs-098", FirstName = "احمدرضا", LastName = "عابدزاده" }); lst.Add(new Person() { Id = 2, Code = "fTuuuw-652", FirstName = "کریم", LastName = "باقری" }); lst.Add(new Person() { Id = 3, Code = "Lopapo-123", FirstName = "خداداد", LastName = "عزیزی" }); lst.Add(new Person() { Id = 4, Code = "Utppq-981", FirstName = "علی", LastName = "دایی" }); lst.Add(new Person() { Id = 5, Code = "zttsn-471", FirstName = "علی", LastName = "کریمی" }); lst.Add(new Person() { Id = 6, Code = "poiud-901", FirstName = "مهدی", LastName = "مهدوی کیا" }); lst.Add(new Person() { Id = 7, Code = "wqrPoP-391", FirstName = "علیرضا", LastName = "منصوریان" }); return lst; }
... <div id="div_SearchBoxContainer"> </div> ... @section scripts{ <script type="text/javascript"> $("#div_SearchBoxContainer").searchboxmvc({ loadUrl: '@Url.Action(actionName: "LoadData", controllerName: "Home")', defaultStringFilterMode: "startWith", loadDataOnLeave: true, displayClass: "", displayNoResultClass: "", display: function (element, record) { $(element).html(record.FirstName + " " + record.LastName); }, listItemsDisplay: function (element, record, index) { return record.LastName + " " + record.FirstName + "(" + record.Code + ")"; }, fields: [ { fieldName: "Id", fieldTitle: "شناسه", width: 100, defaultValueField: true }, { fieldName: "FirstName", fieldTitle: "نام", width: 200, defaultDisplayField: true, filter: true, isStringType: true }, { fieldName: "LastName", fieldTitle: "نام خانوادگی", filter: false, isStringType: true } ] }); </script> }
public class AutomapperRegistry : Registry { public AutomapperRegistry() { var platformSpecificRegistry = PlatformAdapter.Resolve<IPlatformSpecificMapperRegistry>(); platformSpecificRegistry.Initialize(); For<ConfigurationStore>().Singleton().Use<ConfigurationStore>() .Ctor<IEnumerable<IObjectMapper>>().Is(MapperRegistry.Mappers); For<IConfigurationProvider>().Use(ctx => ctx.GetInstance<ConfigurationStore>()); For<IConfiguration>().Use(ctx => ctx.GetInstance<ConfigurationStore>()); For<ITypeMapFactory>().Use<TypeMapFactory>(); For<IMappingEngine>().Singleton().Use<MappingEngine>() .SelectConstructor(() => new MappingEngine(null)); this.Scan(scanner => { scanner.AssembliesFromApplicationBaseDirectory(); scanner.ConnectImplementationsToTypesClosing(typeof(ITypeConverter<,>)) .OnAddedPluginTypes(t => t.HybridHttpOrThreadLocalScoped()); scanner.ConnectImplementationsToTypesClosing(typeof(ValueResolver<,>)) .OnAddedPluginTypes(t => t.HybridHttpOrThreadLocalScoped()); }); } }
public static class SmObjectFactory { private static readonly Lazy<Container> _containerBuilder = new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication); public static IContainer Container { get { return _containerBuilder.Value; } } private static Container defaultContainer() { var container = new Container(cfg => { cfg.AddRegistry<AutomapperRegistry>(); cfg.Scan(scan => { scan.TheCallingAssembly(); scan.WithDefaultConventions(); scan.AddAllTypesOf<Profile>().NameBy(item => item.FullName); }); }); configureAutoMapper(container); return container; } private static void configureAutoMapper(IContainer container) { var configuration = container.TryGetInstance<IConfiguration>(); if (configuration == null) return; //saying AutoMapper how to resolve services configuration.ConstructServicesUsing(container.GetInstance); foreach (var profile in container.GetAllInstances<Profile>()) { configuration.AddProfile(profile); } } }
public class UsersService : IUsersService { private readonly IMappingEngine _mappingEngine; public UsersService(IMappingEngine mappingEngine) { _mappingEngine = mappingEngine; } public UserViewModel GetName(int id) { var dbUser1 = new User { Id = 1, Name = "Test", RegistrationDate = DateTime.Now.AddDays(-10) }; var uiUser = new UserViewModel(); _mappingEngine.Map(source: dbUser1, destination: uiUser); return uiUser; } }
// ... builder.Services.AddRazorComponents().AddInteractiveServerComponents(); // ... app.MapRazorComponents<App>().AddInteractiveServerRenderMode();
// ... builder.Services.AddRazorComponents(); // ... app.MapRazorComponents<App>();
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <base href="/" /> <link rel="stylesheet" href="bootstrap/bootstrap.min.css" /> <link rel="stylesheet" href="app.css" /> <link rel="stylesheet" href="MyApp.styles.css" /> <link rel="icon" type="image/png" href="favicon.png" /> <HeadOutlet /> </head> <body> <Routes /> <script src="_framework/blazor.web.js"></script> </body> </html>
<Router AppAssembly="@typeof(Program).Assembly"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" /> <FocusOnNavigate RouteData="@routeData" Selector="h1" /> </Found> </Router>
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
@page "/StatusCode/{responseCode}" <h3>StatusCode @ResponseCode</h3> @code { [Parameter] public string? ResponseCode { get; set; } }
if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error", createScopeForErrors: true); }
404.razor
@page "/StatusCode/404" <PageTitle>Not found</PageTitle> <h1>Not found</h1> <p role="alert">Sorry, there's nothing at this address.</p>
500.razor
@page "/StatusCode/500" <PageTitle>Unexpected error</PageTitle> <h1>Unexpected error</h1> <p role="alert">There was an unexpected error.</p>
401.razor
@page "/StatusCode/401" <PageTitle>Not Authorized</PageTitle> <h1>Not Authorized</h1> <p role="alert">Sorry, you are not authorized to access this page.</p>
<HeadOutlet @rendermode="@InteractiveServer" /> ... <Routes @rendermode="@InteractiveServer" />
dotnet new blazor --interactivity Server --all-interactive
@rendermode InteractiveServerRenderModeWithoutPrerendering @code{ static readonly IComponentRenderMode InteractiveServerRenderModeWithoutPrerendering = new InteractiveServerRenderMode(false); }
در این مثال به کمک MVC5، یک کپچای ساده و قابل فهم را تولید و استفاده خواهیم کرد. این نوشته بر اساس این مقاله ایجاد شده و جزئیات زیادی برای درک افراد مبتدی به آن افزوده شده است که امیدوارم راهنمای مفیدی برای علاقمندان باشد.
با کلیک راست بر روی پوشه کنترلر، یک کنترلر به منظور ایجاد کپچا بسازید و اکشن متد زیر را در آن کنترلر ایجاد کنید:
public class CaptchaController : Controller { public ActionResult CaptchaImage(string prefix, bool noisy = true) { var rand = new Random((int)DateTime.Now.Ticks); //generate new question int a = rand.Next(10, 99); int b = rand.Next(0, 9); var captcha = string.Format("{0} + {1} = ?", a, b); //store answer Session["Captcha" + prefix] = a + b; //image stream FileContentResult img = null; using (var mem = new MemoryStream()) using (var bmp = new Bitmap(130, 30)) using (var gfx = Graphics.FromImage((Image)bmp)) { gfx.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; gfx.SmoothingMode = SmoothingMode.AntiAlias; gfx.FillRectangle(Brushes.White, new Rectangle(0, 0, bmp.Width, bmp.Height)); //add noise if (noisy) { int i, r, x, y; var pen = new Pen(Color.Yellow); for (i = 1; i < 10; i++) { pen.Color = Color.FromArgb( (rand.Next(0, 255)), (rand.Next(0, 255)), (rand.Next(0, 255))); r = rand.Next(0, (130 / 3)); x = rand.Next(0, 130); y = rand.Next(0, 30); gfx.DrawEllipse(pen, x - r, y - r, r, r); } } //add question gfx.DrawString(captcha, new Font("Tahoma", 15), Brushes.Gray, 2, 3); //render as Jpeg bmp.Save(mem, System.Drawing.Imaging.ImageFormat.Jpeg); img = this.File(mem.GetBuffer(), "image/Jpeg"); } return img; }
همانطور که از کد فوق پیداست، دو مقدار a و b، به شکل اتفاقی ایجاد میشوند و حاصل جمع آنها در یک Session نگهداری خواهد شد. سپس تصویری بر اساس تصویر a+b ایجاد میشود (مثل 3+4). این تصویر خروجی این اکشن متد است. به سادگی میتوانید این اکشن را بر اساس خواسته خود اصلاح کنید؛ مثلا به جای حاصل جمع دو عدد، از کاربرد چند حرف یا عدد که بصورت اتفاقی تولید کردهاید، استفاده نمائید.
فرض کنید میخواهیم کپچا را هنگام ثبت نام استفاده کنیم.
در فایل AccountViewModels.cs در پوشه مدلها در کلاس RegisterViewModel خاصیت زیر را اضافه کنید:
[Required(ErrorMessage = "لطفا {0} را وارد کنید")] [Display(Name = "حاصل جمع")] public string Captcha { get; set; }
حالا در پوشه View/Account به فایل Register.Cshtml خاصیت فوق را اضافه کنید:
<div class="form-group"> <input type="button" value="" id="refresh" /> @Html.LabelFor(model => model.Captcha) <img alt="Captcha" id="imgcpatcha" src="@Url.Action("CaptchaImage","Captcha")" style="" /> </div>
وظیفه این بخش، نمایش کپچاست. تگ img دارای آدرسی است که توسط اکشن متدی که در ابتدای این مقاله ایجاد نمودهایم تولید میشود. این آدرس تصویر کپچاست. یک دکمه هم با شناسه refresh برای به روز رسانی مجدد تصویر در نظر گرفتهایم.
حالا کد ایجکسی برای آپدیت کپچا توسط دکمه refresh را به شکل زیر بنویسید (من در پایین ویوی Register، اسکریپت زیر را قرار دادم):
<script type="text/javascript"> $(function () { $('#refresh').click(function () { $.ajax({ url: '@Url.Action("CaptchaImage","Captcha")', type: "GET", data: null }) .done(function (functionResult) { $("#imgcpatcha").attr("src", "/Captcha/CaptchaImage?" + functionResult); }); }); }); </script>
آنچه در url نوشته شده است، شاید اصولیترین شکل فراخوانی یک اکشن متد باشد. این اکشن در ابتدای مقاله تحت کنترلری به نام Captcha معرفی شده بود و خروجی آن آدرس یک فایل تصویری است. نوع ارتباط، Get است و هیچ اطلاعاتی به اکشن متد فرستاده نمیشود، اما اکشن متد ما آدرسی را به ما برمیگرداند که تحت نام FunctionResult آن را دریافت کرده و به کمک کد جی کوئری، مقدارش را در ویژگی src تصویر موجود در صفحه جاری جایگزین میکنیم. دقت کنید که برای دسترسی به تصویر، لازم است جایگزینی آدرس، در ویژگی src به شکل فوق صورت پذیرد.*
تنها کار باقیمانده اضافه کردن کد زیر به ابتدای اکشن متد Register درون کنترلر Account است.
if (Session["Captcha"] == null || Session["Captcha"].ToString() != model.Captcha) { ModelState.AddModelError("Captcha", "مجموع اشتباه است"); }
واضح است که اینکار پیش از شرط if(ModelState.IsValidate) صورت میگیرد و وظیفه شرط فوق، بررسی ِ برابریِ مقدار Session تولید شده در اکشن CaptchaImage (ابتدای این مقاله) با مقدار ورودی کاربر است. (مقداری که از طریق خاصیت تولیدی خودمان به آن دسترسی داریم) . بدیهیاست اگر این دو مقدار نابرابر باشند، یک خطا به ModelState اضافه میشود و شرط ModelState.IsValid که در اولین خط بعد از کد فوق وجود دارد، برقرار نخواهد بود و پیغام خطا در صفحه ثبت نام نمایش داده خواهد شد.
تصویر زیر نمونهی نتیجهای است که حاصل خواهد شد :
* اصلاح : دقت کنید بدون استفاده از ایجکس هم میتوانید تصویر فوق را آپدیت کنید:
$('#refresh').click(function () { var d = new Date(); $("#imgcpatcha").attr("src", "Captcha/CaptchaImage?" + d.getTime()); });
رویداد کلیک را با کد فوق جایگزین کنید؛ دو نکته در اینجا وجود دارد :
یک. استفاده از زمان در انتهای آدرس به خاطر مشکلاتیست که فایرفاکس یا IE با اینگونه آپدیتهای تصویری دارند. این دو مرورگر (بر خلاف کروم) تصاویر را نگهداری میکنند و آپدیت به روش فوق به مشکل برخورد میکند مگر آنکه آدرس را به کمک اضافه کردن زمان آپدیت کنید تا مرورگر متوجه داستان شود
دو. همانطور که میبینید آدرس تصویر در حقیقت خروجی یک اکشن است. پس نیازی نیست هر بار این اکشن را به کمک ایجکس صدا بزنیم و روش فوق در مرورگرهای مختلف جواب خواهد داد.
{"DateTimeUtcTicks":637345812872371593}
g/ibfD2M3uE1RhEGxt8/jKcmpW2zhU1kKjVRC7CyrHiCHkdaAmLOwziBATFnHyJ3
var dateTimeNow = DateTime.UtcNow; var expireTimeFrom = dateTimeNow.AddMinutes(-1).Ticks; var expireTimeTo = dateTimeNow.Ticks; string clientSecret = httpContext.Request.Headers["ClientSecret"].ToString(); string decryptedRequestHeader = AesProvider.Decrypt(requestKeyHeader, clientSecret); var requestKeyData = System.Text.Json.JsonSerializer.Deserialize<ApiLimiterDto>(decryptedRequestHeader); if (requestKeyData.DateTimeUtcTicks >= expireTimeFrom && requestKeyData.DateTimeUtcTicks <= expireTimeTo)
public class ApiLimiterDto { public long DateTimeUtcTicks { get; set; } }
public class ApiLimiterMiddleware { private readonly RequestDelegate _next; private readonly IDistributedCache _cache; public ApiLimiterMiddleware(RequestDelegate next, IDistributedCache cache) { _next = next; _cache = cache; } private const string requestKey = "Request-Key"; private const string clientSecretHeader = "ClientSecret"; public async Task InvokeAsync(HttpContext httpContext) { if (!httpContext.Request.Headers.ContainsKey(requestKey) || !httpContext.Request.Headers.ContainsKey(clientSecretHeader)) { await WriteToReponseAsync(); return; } var requestKeyHeader = httpContext.Request.Headers[requestKey].ToString(); string clientSecret = httpContext.Request.Headers[clientSecretHeader].ToString(); if (string.IsNullOrEmpty(requestKeyHeader) || string.IsNullOrEmpty(clientSecret)) { await WriteToReponseAsync(); return; } //اگر کلید در کش موجود بود یعنی کاربر از کلید تکراری استفاده کرده است if (_cache.GetString(requestKeyHeader) != null) { await WriteToReponseAsync(); return; } var dateTimeNow = DateTime.UtcNow; var expireTimeFrom = dateTimeNow.AddMinutes(-1).Ticks; var expireTimeTo = dateTimeNow.Ticks; string decryptedRequestHeader = AesProvider.Decrypt(requestKeyHeader, clientSecret); var requestKeyData = System.Text.Json.JsonSerializer.Deserialize<ApiLimiterDto>(decryptedRequestHeader); if (requestKeyData.DateTimeUtcTicks >= expireTimeFrom && requestKeyData.DateTimeUtcTicks <= expireTimeTo) { //ذخیره کلید درخواست در کش برای جلوگیری از استفاده مجدد از کلید await _cache.SetAsync(requestKeyHeader, Encoding.UTF8.GetBytes("KeyExist"), new DistributedCacheEntryOptions { AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(2) }); await _next(httpContext); } else { await WriteToReponseAsync(); return; } async Task WriteToReponseAsync() { httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; await httpContext.Response.WriteAsync("Forbidden: You don't have permission to call this api"); } } }
public static class AesProvider { private static byte[] GetIV() { //این کد ثابتی است که باید در سمت سرور و موبایل موجود باشد return encoding.GetBytes("ThisIsASecretKey"); } public static string Encrypt(string plainText, string key) { try { var aes = GetRijndael(key); ICryptoTransform AESEncrypt = aes.CreateEncryptor(aes.Key, aes.IV); byte[] buffer = encoding.GetBytes(plainText); string encryptedText = Convert.ToBase64String(AESEncrypt.TransformFinalBlock(buffer, 0, buffer.Length)); return encryptedText; } catch (Exception) { throw new Exception("an error occurred when encrypting"); } } private static RijndaelManaged GetRijndael(string key) { return new RijndaelManaged { KeySize = 128, BlockSize = 128, Padding = PaddingMode.PKCS7, Mode = CipherMode.CBC, Key = encoding.GetBytes(key), IV = GetIV() }; } private static readonly Encoding encoding = Encoding.UTF8; public static string Decrypt(string plainText, string key) { try { var aes = GetRijndael(key); ICryptoTransform AESDecrypt = aes.CreateDecryptor(aes.Key, aes.IV); byte[] buffer = Convert.FromBase64String(plainText); return encoding.GetString(AESDecrypt.TransformFinalBlock(buffer, 0, buffer.Length)); } catch (Exception) { throw new Exception("an error occurred when decrypting"); } } }
public class ApiLimiterPipeline { public void Configure(IApplicationBuilder app) { app.UseMiddleware<ApiLimiterMiddleware>(); } }
نحوه استفاده از میان افزار برای یک اکشن خاص :
[Route("api/[controller]/[action]")] [ApiController] public class ValuesController : ControllerBase { [MiddlewareFilter(typeof(ApiLimiterPipeline))] public async Task<IActionResult> Get() { return Ok("Hi"); } }