راه حلهای مختلفی برای مدیریت دسترسیهای پویا وجود دارد که باتوجه به Domain ای که در آن قرار داریم میتوانند مورد استفاده قرار بگیرند و در این رابطه مطالب زیادی هم ارائه شده است؛ در این راستا یکی از راه حل هایی که در یکی سیستمها استفاده کرده ایم را در این gist میتوانید مشاهده کنید.
در راه حل ارائه شده، علاوه بر این که امکان اعطای یکسری دسترسی برای گروههای کاربری وجود دارد، میتوان دسترسی هایی را به صورت مستقیم برای کاربر خاصی نیز اعطاء کرد یا یکسری از دسترسیهای کاربر را که از طریق گروههای کاربری خود دارا میباشد را از او گرفت و ...
public static class PermissionNames { public const string Pages = nameof(Pages); public const string Pages_Administration = nameof(Pages_Administration); public const string Pages_Administration_Roles = nameof(Pages_Administration_Roles); public const string Pages_Administration_Roles_Create = nameof(Pages_Administration_Roles_Create); public const string Pages_Administration_Roles_Edit = nameof(Pages_Administration_Roles_Create); public const string Pages_Administration_Roles_Delete = nameof(Pages_Administration_Roles_Create); public const string Pages_Administration_Users = nameof(Pages_Administration_Users); public const string Pages_Administration_Users_Create = nameof(Pages_Administration_Users_Create); public const string Pages_Administration_Users_Edit = nameof(Pages_Administration_Users_Create); public const string Pages_Administration_Users_Delete = nameof(Pages_Administration_Users_Create); //... }
import { decorate } from "mobx"; class Count { value = 0; } decorate(Count, { value: observable }); const count = new Count();
بازنویسی مثال ورود متن و نمایش آن با Mobx decorators
در اینجا یک text-box، به همراه دو div در صفحه رندر خواهند شد که قرار است با ورود اطلاعاتی در text-box، یکی از آنها (text-display) این اطلاعات را به صورت معمولی و دیگری (text-display-uppercase) آنرا به صورت uppercase نمایش دهد. روش کار انجام شده هم مستقل از React است و به صورت مستقیم با استفاده از DOM API عمل شدهاست. این مثال را پیشتر در اولین قسمت بررسی MobX، ملاحظه کردید. اکنون اگر بخواهیم بجای شیءای که توسط متد observable کتابخانهی MobX محصور شدهاست:
const text = observable({ value: "Hello world!", get uppercase() { return this.value.toUpperCase(); } });
ابتدا یک پروژهی جدید React را ایجاد میکنیم:
> create-react-app state-management-with-mobx-part3 > cd state-management-with-mobx-part3 > npm start
> npm install --save mobx
<!DOCTYPE html> <html lang="en"> <head> <title>MobX Basics, part 3</title> <meta charset="UTF-8" /> <link href="src/styles.css" /> </head> <body> <main> <input id="text-input" /> <p id="text-display"></p> <p id="text-display-uppercase"></p> </main> <script src="src/index.js"></script> </body> </html>
import { autorun, computed, observable } from "mobx"; const input = document.getElementById("text-input"); const textDisplay = document.getElementById("text-display"); const loudDisplay = document.getElementById("text-display-uppercase"); class Text { @observable value = "Hello World"; @computed get uppercase() { return this.value.toUpperCase(); } } const text = new Text(); input.addEventListener("keyup", event => { text.value = event.target.value; }); autorun(() => { input.value = text.value; textDisplay.textContent = text.value; loudDisplay.textContent = text.uppercase; });
اکنون اگر در همین حال، برنامه را با دستور npm start اجرا کنیم، با خطای زیر متوقف خواهیم شد:
./src/index.js SyntaxError: \src\index.js: Support for the experimental syntax 'decorators-legacy' isn't currently enabled (8:3): 6 | 7 | class Text { > 8 | @observable value = "Hello World"; | ^ 9 | @computed get uppercase() { 10 | return this.value.toUpperCase(); 11 | }
راه حل اول: از Decorators استفاده نکنیم!
یک راه حل مشکل فوق این است که بدون هیچ تغییری در ساختار پروژهی React خود، اصلا از decorator syntax استفاده نکنیم. برای مثال اگر یک کلاس متداول MobX ای چنین شکلی را دارد:
import { observable, computed, action } from "mobx"; class Timer { @observable start = Date.now(); @observable current = Date.now(); @computed get elapsedTime() { return this.current - this.start + "milliseconds"; } @action tick() { this.current = Date.now(); } }
import { observable, computed, action, decorate } from "mobx"; class Timer { start = Date.now(); current = Date.now(); get elapsedTime() { return this.current - this.start + "milliseconds"; } tick() { this.current = Date.now(); } } decorate(Timer, { start: observable, current: observable, elapsedTime: computed, tick: action });
همچنین در این حالت بجای استفاده از کامپوننتهای کلاسی، باید از روش بکارگیری متد observer برای محصور کردن کامپوننت تابعی تعریف شده استفاده کرد (تا دیگر نیازی به ذکر observer class@ نباشد):
const Counter = observer(({ count }) => { return ( // ... ); });
راه حل دوم: از تایپاسکریپت استفاده کنید!
create-react-app امکان ایجاد پروژههای React تایپاسکریپتی را با ذکر سوئیچ typescript نیز دارد:
> create-react-app my-proj1 --typescript
{ "compilerOptions": { // ... "experimentalDecorators": true } }
فعالسازی MobX Decorators در پروژههای استاندارد React مبتنی بر ES6
MobX از legacy" decorators spec" پشتیبانی میکند. یعنی اگر پروژهای از spec جدید استفاده کند، دیگر نخواهد توانست با MobX فعلی کار کند. این هم مشکل MobX نیست. مشکل اینجا است که باید دانست کلا decorators در زبان جاوااسکریپت هنوز در مرحلهی آزمایشی قرار دارند و تکلیف spec نهایی و تائید شدهی آن مشخص نیست.
برای فعالسازی decorators در یک پروژهی React استاندارد مبتنی بر ES6، شاید کمی جستجو کنید و به نتایجی مانند افزودن فایل babelrc. به ریشهی پروژه و نصب افزونههایی مانند babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties@ برسید. اما ... اینها بدون اجرای دستور npm run eject کار نمیکنند و اگر این دستور را اجرا کنیم، در نهایت به یک فایل package.json بسیار شلوغ خواهیم رسید (اینبار ارجاعات به Babel، Webpack و تمام ابزارهای دیگر نیز ظاهر میشوند). همچنین این عملیات نیز یک طرفهاست. یعنی از این پس قرار است کنترل تمام این پشت صحنه، در اختیار ما باشد و به روز رسانیهای بعدی create-react-app را با مشکل مواجه میکند. این گزینه صرفا مختص توسعه دهندگان پیشرفتهی React است. به همین جهت نیاز به روشی را داریم تا بتوانیم تنظیمات Webpack و کامپایلر Babel را بدون اجرای دستور npm run eject، تغییر دهیم تا در نتیجه، decorators را در آن فعال کنیم و خوشبختانه پروژهی react-app-rewired دقیقا برای همین منظور طراحی شدهاست.
بنابراین ابتدا بستههای زیر را نصب میکنیم:
> npm i --save-dev customize-cra react-app-rewired
پس از نصب این پیشنیازها، فایل جدید config-overrides.js را به ریشهی پروژه، جائیکه فایل package.json قرار گرفتهاست، با محتوای زیر اضافه کنید تا پشتیبانی ازlegacy" decorators spec" فعال شوند:
const { override, addDecoratorsLegacy, disableEsLint } = require("customize-cra"); module.exports = override( // enable legacy decorators babel plugin addDecoratorsLegacy(), // disable eslint in webpack disableEsLint() );
"scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-app-rewired eject" },
تنظیمات ESLint مخصوص کار با decorators
فایل ویژهی eslintrc.json. که در ریشهی پروژه قرار میگیرد (این فایل بدون نام است و فقط از پسوند تشکیل شده)، برای پروژههای MobX، باید حداقل تنظیم زیر را داشته باشد تا ESLint بتواند legacyDecorators را نیز پردازش کند:
{ "extends": "react-app", "parserOptions": { "ecmaFeatures": { "legacyDecorators": true } } }
{ "env": { "node": true, "commonjs": true, "browser": true, "es6": true, "mocha": true }, "settings": { "react": { "version": "detect" } }, "parserOptions": { "ecmaFeatures": { "jsx": true, "legacyDecorators": true }, "ecmaVersion": 2018, "sourceType": "module" }, "plugins": [ "babel", "react", "react-hooks", "react-redux", "no-async-without-await", "css-modules", "filenames", "simple-import-sort" ], "rules": { "no-const-assign": "warn", "no-this-before-super": "warn", "constructor-super": "warn", "strict": [ "error", "safe" ], "no-debugger": "error", "brace-style": [ "error", "1tbs", { "allowSingleLine": true } ], "no-trailing-spaces": "error", "keyword-spacing": "error", "space-before-function-paren": [ "error", "never" ], "spaced-comment": [ "error", "always" ], "vars-on-top": "error", "no-undef": "error", "no-undefined": "warn", "comma-dangle": [ "error", "never" ], "quotes": [ "error", "double" ], "semi": [ "error", "always" ], "guard-for-in": "error", "no-eval": "error", "no-with": "error", "valid-typeof": "error", "no-unused-vars": "error", "no-continue": "warn", "no-extra-semi": "warn", "no-unreachable": "warn", "no-unused-expressions": "warn", "max-len": [ "warn", 80, 4 ], "react/prefer-es6-class": "warn", "react/jsx-boolean-value": "warn", "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn", "react/prop-types": "off", "react-redux/mapDispatchToProps-returns-object": "off", "react-redux/prefer-separate-component-file": "off", "no-async-without-await/no-async-without-await": "warn", "css-modules/no-undef-class": "off", "filenames/match-regex": [ "off", "^[a-zA-Z]+\\.*\\b(typescript|module|locale|validate|test|action|api|reducer|saga)?\\b$", true ], "filenames/match-exported": "off", "filenames/no-index": "off", "simple-import-sort/sort": "error" }, "extends": [ "react-app", "eslint:recommended", "plugin:react/recommended", "plugin:react-redux/recommended", "plugin:css-modules/recommended" ], "globals": { "Atomics": "readonly", "SharedArrayBuffer": "readonly", "process": true } }
>npm i --save-dev eslint babel-eslint eslint-config-react-app eslint-loader eslint-plugin-babel eslint-plugin-react eslint-plugin-css-modules eslint-plugin-filenames eslint-plugin-flowtype eslint-plugin-import eslint-plugin-no-async-without-await eslint-plugin-react-hooks eslint-plugin-react-redux eslint-plugin-redux-saga eslint-plugin-simple-import-sort eslint-loader typescript
const { override, addDecoratorsLegacy, useEslintRc } = require("customize-cra"); module.exports = override( addDecoratorsLegacy(), useEslintRc(".eslintrc.json") );
رفع اخطار مرتبط با decorators در VSCode
تا اینجا کار تنظیم کامپایلر babel، جهت پردازش decorators انجام شد. اما خود VSCode نیز چنین اخطاری را در پروژههایی که از decorates استفاده میکنند، نمایش میدهد:
Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning.ts(1219)
{ "compilerOptions": { "experimentalDecorators": true, "allowJs": true } }
کدهای کامل این قسمت را میتوانید از اینجا دریافت کنید: state-management-with-mobx-part3.zip
EF Code First #12
var container = new Container(x => { // تنظیمات در اینجا });
public static class ObjectFactory { private static readonly Lazy<Container> _containerBuilder = new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication); public static IContainer Container { get { return _containerBuilder.Value; } } private static Container defaultContainer() { return new Container(x => { // تنظیمات در اینجا }); } }
از متدهای همزمان متداول برای انجام امور ذیل استفاده نمائید:
- جهت پردازش اعمالی ساده و سریع
- اعمال مدنظر بیشتر قرار است بر روی CPU اجرا شوند و از مرزهای IO سیستم عبور نمیکنند.
و از متدهای غیرهمزمان برای پردازش موارد زیر کمک بگیرید:
- از وب سرویسهایی استفاده میکنید که متدهای نگارش async را نیز ارائه دادهاند.
- عمل مدنظر network-bound و یا I/O-bound است بجای CPU-bound. یعنی از مرزهای IO سیستم عبور میکند.
- نیاز است چندین عملیات را به موازات هم اجرا کرد.
- نیاز است مکانیزمی را جهت لغو یک عملیات طولانی ارائه دهید.
مزایای استفاده از متدهای async در ASP.NET
استفاده از await در ASP.NET، ساختار ذاتی پروتکل HTTP را که اساسا یک synchronous protocol، تغییر نمیدهد. کلاینت، درخواستی را ارسال میکند و باید تا زمان آماده شدن نتیجه و بازگشت آن از طرف سرور، صبر کند. نحوهی تهیهی این نتیجه، خواه async باشد و یا حتی همزمان، از دید مصرف کننده کاملا مخفی است. اکنون سؤال اینجا است که چرا باید از متدهای async استفاده کرد؟
- پردازش موازی: میتوان چند Task را مثلا توسط Task.WhenAll به صورت موازی با هم پردازش کرده و در نهایت نتیجه را سریعتر به مصرف کننده بازگشت داد. اما باید دقت داشت که این Taskها اگر I/O bound باشند، ارزش پردازش موازی را دارند و اگر compute bound باشند (اعمال محاسباتی)، صرفا یک سری ترد را ایجاد و مصرف کردهاید که میتوانستهاند به سایر درخواستهای رسیده پاسخ دهند.
- خالی کردن تردهای در حال انتظار: در اعمالی که disk I/O و یا network I/O دارند، پردازش موازی و اعمال async به شدت مقیاس پذیری سیستم را بالا میبرند. به این ترتیب worker thread جاری (که تعداد آنها محدود است)، سریعتر آزاد شده و به worker pool بازگشت داده میشود تا بتواند به یک درخواست دیگر رسیده سرویس دهد. در این حالت میتوان با منابع کمتری، درخواستهای بیشتری را پردازش کرد.
ایجاد Asynchronous HTTP Handlers در ASP.Net 4.5
در نگارشهای پیش از دات نت 4.5، برای نوشتن فایلهای ashx غیرهمزمان میبایستی اینترفیس IHttpAsynchHandler پیاده سازی میشد که نحوهی کار با آن از مدل APM پیروی میکرد؛ نیاز به استفاده از یک سری callback داشت و این عملیات باید طی دو متد پردازش میشد. اما در دات نت 4.5 و با معرفی امکانات async و await، نگارش سازگاری با پیاده سازی کلاس پایه HttpTaskAsyncHandler فراهم شده است.
برای آزمایش آن، یک برنامهی جدید ASP.NET Web forms نگارش 4.5 یا بالاتر را ایجاد کنید. سپس از منوی پروژه، گزینهی Add new item یک Generic handler به نام LogRequestHandler.ashx را به پروژه اضافه نمائید.
زمانیکه این فایل به پروژه اضافه میشود، یک چنین امضایی را دارد:
public class LogRequestHandler : IHttpHandler
using System; using System.Net; using System.Text; using System.Threading.Tasks; using System.Web; namespace Async14 { public class LogRequestHandler : HttpTaskAsyncHandler { public override async Task ProcessRequestAsync(HttpContext context) { string url = context.Request.QueryString["rssfeedURL"]; if (string.IsNullOrWhiteSpace(url)) { context.Response.Write("Rss feed URL is not provided"); } using (var webClient = new WebClient {Encoding = Encoding.UTF8}) { webClient.Headers.Add("User-Agent", "LogRequestHandler 1.0"); var rssfeed = await webClient.DownloadStringTaskAsync(url); context.Response.Write(rssfeed); } } public override bool IsReusable { get { return true; } } public override void ProcessRequest(HttpContext context) { throw new Exception("The ProcessRequest method has no implementation."); } } }
در این مثال آدرس یک فید RSS از طریق کوئری استرینگ rssfeedURL دریافت شده و سپس محتوای آن به کمک متد DownloadStringTaskAsync دریافت و بازگشت داده میشود.
برای آزمایش آن، مسیر ذیل را درخواست دهید:
http://localhost:4207/LogRequestHandler.ashx?rssfeedURL=https://www.dntips.ir/feed/latestchanges
صفحات async در ASP.NET 4.5
در قسمتهای قبل مشاهده کردیم که در برنامههای دسکتاپ، به سادگی میتوان امضای روالهای رخداد گردان را به async تغییر داد و ... برنامه کار میکند. به علاوه از مزیت استفاده از واژه کلیدی await نیز در آنها برخوردار خواهیم شد. اما ... هرچند این روش در وب فرمها نیز صادق است (مثلا public void Page_Load را به public async void Page_Load میتوان تبدیل کرد) اما اعضای تیم ASP.NET آنرا در مورد برنامههای وب فرم توصیه نمیکنند:
Async void event handlers تنها در مورد تعداد کمی از روالهای رخدادگردان ASP.NET Web forms کار میکنند و از آنها تنها برای تدارک پردازشهای ساده میتوان استفاده کرد. اگر کار در حال انجام اندکی پیچیدگی دارد، «باید» از PageAsyncTask استفاده نمائید. علت اینجا است که Async void یعنی fire and forget (کاری را شروع کرده و فراموشش کنید). این روش در برنامههای دسکتاپ کار میکند، زیرا این برنامهها مدل طول عمر متفاوتی داشته و تا زمانیکه برنامه از طرف OS خاتمه نیابد، مشکلی نخواهند داشت. اما برنامههای بدون حالت وب متفاوتند. اگر عملیات async پس از خاتمهی طول عمر صفحه پایان یابد، دیگر نمیتوان اطلاعات صحیحی را به کاربر ارائه داد. بنابراین تا حد ممکن از تعاریف async void در برنامههای وب خودداری کنید.
تبدیل روالهای رخدادگردان متداول وب فرمها به نسخهی async شامل دو مرحله است:
الف) از متد جدید RegisterAsyncTask که در کلاس پایه Page قرار دارد برای تعریف یک PageAsyncTask استفاده کنید:
using System; using System.Net; using System.Text; using System.Threading.Tasks; using System.Web.UI; namespace Async14 { public partial class _default : Page { protected void Page_Load(object sender, EventArgs e) { RegisterAsyncTask(new PageAsyncTask(LoadSomeData)); } public async Task LoadSomeData() { using (var webClient = new WebClient { Encoding = Encoding.UTF8 }) { webClient.Headers.Add("User-Agent", "LogRequest 1.0"); var rssfeed = await webClient.DownloadStringTaskAsync("url"); //listcontacts.DataSource = rssfeed; } } } }
ب) سپس در کدهای فایل aspx، نیاز است خاصیت async را نیز true نمائید:
<%@ Page Language="C#" AutoEventWireup="true" Async="true" CodeBehind="default.aspx.cs" Inherits="Async14._default" %>
تغییر تنظیمات IIS برای بهره بردن از پردازشهای Async
اگر از ویندوزهای 7، ویستا و یا 8 استفاده میکنید، IIS آنها به صورت پیش فرض به 10 درخواست همزمان محدود است.
بنابراین تنظیمات ذیل مرتبط است به یک ویندوز سرور و نه یک work station :
به IIS manager مراجعه کنید. سپس برگهی Application Pools آنرا باز کرده و بر روی Application pool برنامه خود کلیک راست نمائید. در اینجا گزینهی Advanced Settings را انتخاب کنید. در آن Queue Length را به مثلا عدد 5000 تغییر دهید. همچنین در دات نت 4.5 عدد 5000 برای MaxConcurrentRequestsPerCPU نیز مناسب است. به علاوه عدد connectionManagement/maxconnection را نیز به 12 برابر تعداد هستههای موجود تغییر دهید.
آشنایی با AOP Interceptors
ابزارهایی جهت تولید AOP Interceptors
متداولترین کامپوننتهای خارجی که جهت تولید AOP Interceptors مورد استفاده قرار میگیرند، همان IOC Containers معروف هستند مانند StructureMap، Ninject، MS Unity و غیره.
سایر ابزارهای تولید AOP Interceptors، از روش تولید Dynamic proxies بهره میگیرند. به این ترتیب مزین کنندههایی پویا، در زمان اجرا، کدهای شما را محصور خواهند کرد. (نمونهای از آنرا شاید در حین کار با ORMهای مختلف دیده باشید).
نگاهی به فرآیند Interception
زمانیکه از یک IOC Container در کدهای خود استفاده میکنید، مراحلی چند رخ خواهند داد:
الف) کد فراخوان، از IOC Container، یک شیء مشخص را درخواست میکند. عموما اینکار با درخواست یک اینترفیس صورت میگیرد؛ هرچند محدودیتی نیز وجود نداشته و امکان درخواست یک کلاس از نوعی مشخص نیز وجود دارد.
ب) در ادامه IOC Container به لیست اشیاء قابل ارائه توسط خود نگاه کرده و در صورت وجود، وهله سازی شیء درخواست شده را انجام و نهایتا شیء مطلوب را بازگشت خواهد داد.
ج) سپس، کد فراخوان، وهله دریافتی را مورد پردازش قرار داده و شروع به استفاده از متدها و خواص آن خواهد نمود.
اکنون با اضافه کردن Interception به این پروسه، چند مرحله دیگر نیز در این بین به آن اضافه خواهند شد:
الف) در اینجا نیز در ابتدا کد فراخوان، درخواست وهلهای را بر اساس اینترفیسی خاص به IOC Container ارائه میدهد.
ب) IOC Container نیز سعی در وهله سازی درخواست رسیده بر اساس تنظیمات اولیه خود میکند.
ج) اما در این حالت IOC Container تشخیص میدهد، نوعی که باید بازگشت دهد، علاوه بر وهله سازی، نیاز به مزین سازی توسط Aspects و پیاده سازی Interceptors را نیز دارد. بنابراین نوع مورد انتظار را در صورت وجود، به یک Dynamic Proxy، بجای بازگشت مستقیم به فراخوان ارائه میدهد.
د) در ادامه Dynamic Proxy، نوع مورد انتظار را توسط Interceptors محصور کرده و به فراخوان بازگشت میدهد.
ه) اکنون فراخوان، در حین استفاده از امکانات شیء وهله سازی شده، به صورت خودکار مراحل مختلف اجرای یک Aspect را که در قسمت قبل بررسی شدند، سبب خواهد شد.
نحوه ایجاد Interceptors
برای ایجاد یک Interceptor دو مرحله باید انجام شود:
الف) پیاده سازی یک اینترفیس
ب) اتصال آن به کدهای اصلی برنامه
در ادامه قصد داریم از یک IOC Container معروف به نام StructureMap در یک برنامه کنسول استفاده کنیم. برای دریافت آن نیاز است دستور پاورشل ذیل را در کنسول نوگت ویژوال استودیو فراخوانی کنید:
PM> Install-Package structuremap
البته باید دقت داشت که برای استفاده از StructureMap نیاز است به خواص پروژه مراجعه و سپس حالت Client profile را به Full profile تغییر داد تا برنامه قابل کامپایل باشد.
using System; using StructureMap; namespace AOP00 { public interface IMyType { void DoSomething(string data, int i); } public class MyType : IMyType { public void DoSomething(string data, int i) { Console.WriteLine("DoSomething({0}, {1});", data, i); } } class Program { static void Main(string[] args) { ObjectFactory.Initialize(x => { x.For<IMyType>().Use<MyType>(); }); var myType = ObjectFactory.GetInstance<IMyType>(); myType.DoSomething("Test", 1); } } }
در اینجا یک اینترفیس نمونه و پیاده سازی آنرا ملاحظه میکنید. همچنین نحوه آغاز تنظیمات StructureMap و نحوه دریافت یک وهله متناظر با IMyType نیز بیان شدهاند.
نکتهی مهمی که در اینجا باید به آن دقت داشت، وضعیت شیء myType حین فراخوانی متد myType.DoSomething است. شیء myType در اینجا، دقیقا یک وهلهی متداول از کلاس myType است و هیچگونه دخل و تصرفی در نحوه اجرای آن صورت نگرفته است.
خوب! تا اینجای کار را احتمالا پیشتر نیز دیده بودید. در ادامه قصد داریم یک Interceptor را طراحی و مراحل چهارگانه اجرای یک Aspect را در اینجا بررسی کنیم.
در ادامه نیاز خواهیم داشت تا یک Dynamic proxy را نیز مورد استفاده قرار دهیم؛ از این جهت که StructureMap تنها دارای Interceptorهای وهله سازی اطلاعات است و نه Method Interceptor. برای دسترسی به Method Interceptors نیاز به یک Dynamic proxy نیز میباشد. در اینجا از Castle.Core استفاده خواهیم کرد:
PM> Install-Package Castle.Core
سپس کلاس ذیل را به پروژه جاری اضافه کنید:
using System; using Castle.DynamicProxy; namespace AOP00 { public class LoggingInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { try { Console.WriteLine("Logging On Start."); invocation.Proceed(); //فراخوانی متد اصلی در اینجا صورت میگیرد Console.WriteLine("Logging On Success."); } catch (Exception ex) { Console.WriteLine("Logging On Error."); throw; } finally { Console.WriteLine("Logging On Exit."); } } } }
اکنون برای معرفی این کلاس به برنامه کافی است سطرهای ذیل را اندکی ویرایش کنیم:
static void Main(string[] args) { ObjectFactory.Initialize(x => { var dynamicProxy = new ProxyGenerator(); x.For<IMyType>().Use<MyType>(); x.For<IMyType>().EnrichAllWith(myTypeInterface => dynamicProxy.CreateInterfaceProxyWithTarget(myTypeInterface, new LoggingInterceptor())); }); var myType = ObjectFactory.GetInstance<IMyType>(); myType.DoSomething("Test", 1); }
برای مثال اکنون با فراخوانی متد myType.DoSomething، ابتدا کنترل برنامه به پروکسی پویای تشکیل شده توسط Castle.Core منتقل میشود. در اینجا هنوز هم متد DoSomething فراخوانی نشده است. ابتدا وارد بدنه متد public void Intercept خواهیم شد. سپس سطر invocation.Proceed، فراخوانی واقعی متد DoSomething اصلی را انجام میدهد. در ادامه باز هم فرصت داریم تا مراحل موفقیت، خطا یا خروج را لاگ کنیم.
تنها زمانیکه کار متد public void Intercept به پایان میرسد، سطر پس از فراخوانی متد myType.DoSomething اجرا خواهد شد.
در این حالت اگر برنامه را اجرا کنیم، چنین خروجی را نمایش میدهد:
Logging On Start. DoSomething(Test, 1); Logging On Success. Logging On Exit.
برای اینکه فراخوانی قسمت On Error را نیز ملاحظه کنید، یک استثنای عمدی را در متد DoSomething قرار داده و مجددا برنامه را اجرا کنید.
کلیدهای مربوط به Request
ضروری؟ | نام کلید | مقدار |
بله | "owin.RequestBody" | یک Stream همراه با request body. اگر body برای request وجود نداشته باشد، Stream.Null به عنوان placeholder قابل استفاده است. |
بله | "owin.RequestHeaders" | یک دیکشنری به صورت IDictionary<string, string[]> از هدرهای درخواست. |
بله | "owin.RequestMethod" | رشتهایی حاوی نوع فعل متد HTTP مربوط به درخواست (مانند GET and POST ) |
بله | "owin.RequestPath" | path درخواست شده به صورت string |
بله | "owin.RequestPathBase" | قسمتی از path درخواست به صورت string |
بله | "owin.RequestProtocol" | نام و نسخهی پروتکل (مانند HTTP/1.0 or HTTP/1.1 ) |
بله | "owin.RequestQueryString" | رشتهای حاوی query string ؛ بدون علامت ? (مانند foo=bar&baz=quux ) |
بله | "owin.RequestScheme" | رشتهایی حاوی URL scheme استفاده شده در درخواست (مانند HTTP or HTTPS ) |
ضروری؟ | نام کلید | مقدار |
بله | "owin.ResponseBody" | یک Stream جهت نوشتن response body در خروجی |
بله | "owin.ResponseHeaders" | یک دیکشنری به صورت IDictionary<string, string[]> از هدرهای response |
خیر | "owin.ResponseStatusCode" | یک عدد صحیح حاوی کد وضعیت HTTP response ؛ حالت پیشفرض 200 است. |
خیر | "owin.ResponseReasonPhrase" | یک رشته حاوی reason phrase مربوط به status code ؛ اگر خالی باشد در نتیجه سرور بهتر است آن را مقداردهی کند. |
خیر | "owin.ResponseProtocol" | یک رشته حاوی نام و نسخهی پروتکل (مانند HTTP/1.0 or HTTP/1.1 )؛ اگر خالی باشد؛ “owin.RequestProtocol” به عنوان مقدار پیشفرض در نظر گرفته خواهد شد. |
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net461" /> <package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net461" /> <package id="Owin" version="1.0" targetFramework="net461" />
using Owin; namespace SimpleOwinWebApp { public class Startup { public void Configuration(IAppBuilder app) { } } }
using Owin; namespace SimpleOwinWebApp { public class Startup { public void Configuration(IAppBuilder app) { app.Use(async (ctx, next) => { await ctx.Response.WriteAsync("Hello"); }); } }
Func<IOwinContext, Func<Task>, Task> handler
app.Use(async (ctx, next) => { var response = ctx.Environment["owin.ResponseBody"] as Stream; using (var writer = new StreamWriter(response)) { await writer.WriteAsync("Hello"); } });
using System; using Microsoft.Owin.Hosting; namespace SimpleOwinConsoleApp { class Program { static void Main(string[] args) { using (WebApp.Start<Startup>("http://localhost:12345")) { Console.WriteLine("Listening to port 12345"); Console.WriteLine("Press Enter to end..."); Console.ReadLine(); } } } }
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace SimpleOwinCoreApp { public class Startup { public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.Run(async (context) => { await context.Response.WriteAsync("Hello World!"); }); } } }
using System.Threading.Tasks; using Microsoft.AspNetCore.Http; namespace SimpleOwinCoreApp.Middlewares { public class SimpleMiddleware { private readonly RequestDelegate _next; public SimpleMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext ctx) { // قبل از فراخوانی میانافزار بعدی await ctx.Response.WriteAsync("Hello DNT!"); await _next(ctx); // بعد از فراخوانی میانافزار بعدی } } }
app.UseMiddleware<SimpleMiddleware>();
"Microsoft.AspNetCore.Owin": "1.0.0"
app.UseOwin(pipeline => { pipeline(next => new MyKatanaBasedMiddleware(next).Invoke) });
using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; namespace SimpleOwinAspNetCore.Middleware { public class IpBlockerMiddleware { private readonly RequestDelegate _next; private readonly IpBlockerOptions _options; public IpBlockerMiddleware(RequestDelegate next, IpBlockerOptions options) { _next = next; _options = options; } public async Task Invoke(HttpContext context) { var ipAddress = context.Request.Host.Host; if (IsBlockedIpAddress(ipAddress)) { context.Response.StatusCode = 403; await context.Response.WriteAsync("Forbidden : The server understood the request, but It is refusing to fulfill it."); return; } await _next.Invoke(context); } private bool IsBlockedIpAddress(string ipAddress) { return _options.Ips.Any(ip => ip == ipAddress); } } }
using System.Collections.Generic; namespace SimpleOwinAspNetCore.Middleware { public class IpBlockerOptions { public IpBlockerOptions() { Ips = new[] { "192.168.1.1" }; } public IList<string> Ips { get; set; } } }
using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; namespace SimpleOwinAspNetCore.Middleware { public static class IpBlockerExtensions { public static IApplicationBuilder UseIpBlocker(this IApplicationBuilder builder, IConfigurationRoot configuration, IpBlockerOptions options = null) { return builder.UseMiddleware<IpBlockerMiddleware>(options ?? new IpBlockerOptions { Ips = configuration.GetSection("block_list").GetChildren().Select(p => p.Value).ToArray() }); } } }
{ "block_list": [ "192.168.1.1", "localhost", "127.0.0.1", "172.16.132.151" ] }
public IConfigurationRoot Configuration { set; get; } public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("blockedIps.json"); Configuration = builder.Build(); }
app.UseIpBlocker(Configuration);
LocalDB چیست؟
- ایا LocalDB جایگزینی برای SqlExpress خواهد بود در ادامه؟ (اینجا انتهای پاراگراف اول منظورش چیه دقیقا؟
SQL Server ExpressLocalDB should be used in place of the SQL Server Express user instance feature which is deprecated )
- هیچ شکلی نمیتوان تحت شبکه کار کرد با این نسخه؟ مثلا جایی که قراره 3 تا سیستم بصورت کلاینت/سرور کار کنن نمیشه فایل دیتابیس روی یک سیستم باشه و از رو 2تای دیگه بهش کانکت شد؟ سنارویی که خیلی وقتا ممکنه استفاده بشه
- بهترین راه برای سناریوهایی مثل مورد 2 که مثال زدم چیه؟ (یک محیط و یک برنامه معمولی که قرار نیست sql server بصورت full نصب بشه رو سرور) آیا میشه با خیال راحت از SqlServer Express Edition استفاده کرد؟
TypeScript 2.4 منتشر شد
چرا vuetify ؟
دلایل زیادی برای استفاده از این framework وجود دارد که از جمله آنها میتوان به مواردی از قبیل رابط کاربری خوب برای طرح بندی صفحه برنامه، پشتیبانی از تمام مرورگرها، پشتیبانی از RTL (راست به چپ کردن صفحه)، پشتیبانی از cli3 و موارد دیگر میتوان اشاره نمود.
روش نصب vuetify
vue add vuetify
ساختار grid
grid برای طرح بندی، یا بخش بندی محتوای برنامه استفاده میشود .vuetify همانند bootstrap، از سیستم بخش بندی 12 تایی برای تقسیم بندی صفحه استفاده میکند. در این روش ما به 5 حالت مختلف میتوانیم صفحه را بخش بندی کنیم:
طریقهی استفاده
ساختار برنامهی ما شامل یک سری از کامپوننتهای از پیش تعیین شده برای راحتی و سادگی کار میباشد که در زیر به آنها اشاره شدهاست.
ساختار برنامه ما باید دارای یک v-container باشد تا به درستی کار کند. این کامپوننت در بر گیرندهی تمام صفحهی برنامه است. هر کامپوننت میتواند تنظیمات خاص خود را داشته باشد.
برای مثال در کد پایین، تنظیم fluid باعث میشود تا کامپوننت v-container تمام عرض صفحهی ما را در بر بگیرد.
<v-container fluid></v-container>
کامپوننت v-layout برای کار با ردیفها مورد استفاده قرار میگیرد که تنظیمات مخصوص به خود را دارد.
برای بخش بندی هر ستون در صفحه میتوان از کامپوننت v-flex استفاده کرد.
نکته: در توضیح کد پایین، در قسمت تعریف v-layout به وسیله row مشخص میکنیم که میخواهیم یک ردیف را ایجاد کنیم و در قسمت تعریف v-flex به وسیله md6 مشخص میکنیم که 6 خانه از 12 خانه موجود در ردیف را میخواهیم در اختیار داشته باشیم:
<v-container> <v-layout row> <v-flex md6> ... </v-flex> <v-flex md6> ... </v-flex> </v-layout> </v-container>
نتیجهی قطعه کد بالا بدین ترتیب است:
به وسیله breakpointها میتوانیم مشخص کنیم که هر المان درون هر مدیا (موبایل، تبلت، کامپیوتر و ...) به چه صورتی نمایش داده شود. در حالت کلی پنج نوع breakpoint وجود دارند که به ترتیب شامل:
<v-flex xs6 sm5> </v-flex>
<v-flex xs5 md8> </v-flex>
<div id="app"> <v-app id="inspire"> <v-container grid-list-xl text-xs-center> <v-layout row wrap> <v-flex xs10 offset-xs1> <v-card dark color="purple"> <v-card-text>xs10 offset-xs1</v-card-text> </v-card> </v-flex> <v-flex xs7 offset-xs5 offset-md2 offset-lg5> <v-card dark color="secondary"> <v-card-text>xs7 offset-(xs5 | md2 | lg5)</v-card-text> </v-card> </v-flex> <v-flex xs12 sm5 md3> <v-card dark color="primary"> <v-card-text>(xs12 | sm5 | md3)</v-card-text> </v-card> </v-flex> <v-flex xs12 sm5 md5 offset-xs0 offset-lg2> <v-card dark color="green"> <v-card-text>(xs12 | sm5 | md5) offset-(xs0 | lg2)</v-card-text> </v-card> </v-flex> </v-layout> </v-container> </v-app> </div>
نتیجهی قطعه کد بالا بدین صورت است: