در ادامه قصد داریم همان امکان پشتیبانی از تصاویر base64 مدفون شده در صفحات HTML را به کتابخانه XMLWorker نیز اضافه کنیم.
تهیه پردازندههای سفارشی تگهای HTML جهت افزونه XMLWorker
در اینجا کار با ارث بری از کلاس AbstractTagProcessor شروع میشود. سپس کافی است تا متد End این کلاس پایه را override کرده و تگ سفارشی خود را پردازش کنیم. نمونههایی از این نوع پردازندهها را در پوشه itextsharp.xmlworker\iTextSharp\tool\xml\html سورسهای iTextSharp میتوانید ملاحظه کنید و جهت ایده گرفتن بسیار مناسب میباشند.
یکی از این پردازندههای پیش فرض موجود در افزونه XMLWorker کار پردازش تگهای img را انجام میدهد و در کلاس iTextSharp.tool.xml.html.Image قرار گرفته است. این پردازنده به صورت پیش فرض تصاویر base64 را پردازش نمیکند. برای رفع این مشکل میتوان به نحو ذیل عمل کرد:
public class CustomImageTagProcessor : iTextSharp.tool.xml.html.Image { public override IList<IElement> End(IWorkerContext ctx, Tag tag, IList<IElement> currentContent) { IDictionary<string, string> attributes = tag.Attributes; string src; if (!attributes.TryGetValue(HTML.Attribute.SRC, out src)) return new List<IElement>(1); if (string.IsNullOrEmpty(src)) return new List<IElement>(1); if (src.StartsWith("data:image/", StringComparison.InvariantCultureIgnoreCase)) { // data:[<MIME-type>][;charset=<encoding>][;base64],<data> var base64Data = src.Substring(src.IndexOf(",") + 1); var imagedata = Convert.FromBase64String(base64Data); var image = iTextSharp.text.Image.GetInstance(imagedata); var list = new List<IElement>(); var htmlPipelineContext = GetHtmlPipelineContext(ctx); list.Add(GetCssAppliers().Apply(new Chunk((iTextSharp.text.Image)GetCssAppliers().Apply(image, tag, htmlPipelineContext), 0, 0, true), tag, htmlPipelineContext)); return list; } else { return base.End(ctx, tag, currentContent); } } }
در اینجا اگر src یک تگ img با الگوی تصاویر base64 شروع شده باشد، تصویر آن استخراج و تبدیل به وهلهای از تصاویر استاندارد iTextSharp میشود. سپس این تصویر در اختیار htmlPipelineContext قرار داده خواهد شد و یا اگر این تصویر از نوع base64 نباشد، همان متد base.End کلاس پایه، جهت پردازش آن کفایت میکند.
استفاده از یک پردازنده تگ سفارشی HTML افزونه XMLWorker
تا اینجا یک پردازنده جدید تصاویر را ایجاد کردهایم. برای اینکه XMLWorker را از وجود آن مطلع کنیم، نیاز است آنرا به درون htmlPipeline این افزونه تزریق نمائیم که کدهای کامل آنرا در ذیل مشاهده میکنید:
using (var doc = new Document(PageSize.A4)) { var writer = PdfWriter.GetInstance(doc, new FileStream("test.pdf", FileMode.Create)); doc.Open(); var html = @"<img src='data:image/png;base64,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' width='62' height='80' style='float: left; margin-right: 28px;' />"; var tagProcessors = (DefaultTagProcessorFactory)Tags.GetHtmlTagProcessorFactory(); tagProcessors.RemoveProcessor(HTML.Tag.IMG); // remove the default processor tagProcessors.AddProcessor(HTML.Tag.IMG, new CustomImageTagProcessor()); // use our new processor var cssResolver = new StyleAttrCSSResolver(); cssResolver.AddCss(@"code { padding: 2px 4px; }", "utf-8", true); var charset = Encoding.UTF8; var hpc = new HtmlPipelineContext(new CssAppliersImpl(new XMLWorkerFontProvider())); hpc.SetAcceptUnknown(true).AutoBookmark(true).SetTagFactory(tagProcessors); // inject the tagProcessors var htmlPipeline = new HtmlPipeline(hpc, new PdfWriterPipeline(doc, writer)); var pipeline = new CssResolverPipeline(cssResolver, htmlPipeline); var worker = new XMLWorker(pipeline, true); var xmlParser = new XMLParser(true, worker, charset); xmlParser.Parse(new StringReader(html)); } Process.Start("test.pdf");
تعاریف مداخل فایل index.html یک سایت AngularJS 2.0
پروژهای که در اینجا در حال استفاده است یک برنامهی ASP.NET MVC 5.x است؛ اما الزامی هم به استفادهی از آن وجود ندارد. یا یک فایل index.html را به ریشهی پروژه اضافه کنید و یا فایل Views\Shared\_Layout.cshtml را به نحو ذیل تغییر دهید:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>@ViewBag.Title - My ASP.NET Application</title> <link href="~/node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet" /> <link href="~/app/app.component.css" rel="stylesheet"/> <link href="~/Content/Site.css" rel="stylesheet" type="text/css" /> <!-- 1. Load libraries --> <!-- IE required polyfills, in this exact order --> <script src="~/node_modules/angular2/es6/dev/src/testing/shims_for_IE.js"></script> <script src="~/node_modules/es6-shim/es6-shim.min.js"></script> <script src="~/node_modules/systemjs/dist/system-polyfills.js"></script> <script src="~/node_modules/angular2/bundles/angular2-polyfills.js"></script> <script src="~/node_modules/systemjs/dist/system.src.js"></script> <script src="~/node_modules/rxjs/bundles/Rx.js"></script> <script src="~/node_modules/angular2/bundles/angular2.dev.js"></script> <!-- Required for http --> <script src="~/node_modules/angular2/bundles/http.dev.js"></script> <!-- Required for routing --> <script src="~/node_modules/angular2/bundles/router.dev.js"></script> <!-- 2. Configure SystemJS --> <script> System.config({ packages: { app: { format: 'register', defaultExtension: 'js' } } }); System.import('app/main') .then(null, console.error.bind(console)); </script> </head> <body> <div> @RenderBody() <pm-app>Loading App...</pm-app> </div> @RenderSection("Scripts", required: false) </body> </html>
سپس کتابخانههای جاوا اسکریپتی مورد نیاز جهت کار با AngularJS 2.0 به ترتیبی که ذکر شده، باید تعریف شوند.
ذکر /~ در ابتدای آدرسها، مختص به ASP.NET MVC است. اگر از آن استفاده نمیکنید، نیازی به ذکر آن هم نیست.
در ادامه تعاریف System.JS ذکر شدهاست. System.JS کار بارگذاری ماژولهای برنامه را به عهده دارد. به این ترتیب دیگر نیازی نیست تا به ازای هر قسمت جدید برنامه، مدخلی را در اینجا اضافه کرد و کار بارگذاری آنها خودکار خواهد بود. فرمت register ایی که در اینجا ذکر شدهاست، تا ماژولهای استاندارد با فرمت ES 6 را نیز پشتیبانی میکند. همچنین با ذکر و تنظیم پسوند پیش فرض به js، دیگر نیازی نخواهد بود تا در حین تعریف importها در قسمتهای مختلف برنامه، پسوند فایلها را به صورت صریح ذکر کرد. مبحث improtها مرتبط است به مفاهیم ماژولها در ES 6 و همچنین TypeScript.
سطر System.import کار بارگذاری اولین ماژول برنامه را از پوشهی app قرار گرفته در ریشهی سایت انجام میدهد. این ماژول main نام دارد.
نوشتن اولین کامپوننت AngularJS 2.0
برنامههای AngularJS 2.0 متشکل هستند از تعدادی کامپوننت و سرویس:
و هر کامپوننت تشکیل شدهاست از:
- یک قالب یا Template: با استفاده از HTML تعریف میشود و کار تشکیل View و نحوهی رندر کامپوننت را مشخص میکند. در این Viewها با استفاده از امکانات binding و directives موجود در AngularJS 2.0 کار دسترسی به دادهها صورت میگیرد.
- یک کلاس: کار این کلاس که توسط TypeScript تهیه میشود، فراهم آوردن کدهای مرتبط با قالب است. برای مثال این کلاس حاوی تعدادی خاصیت خواهد بود که از اطلاعات آنها در View مرتبط استفاده میشود. همچنین این کلاس میتواند حاوی متدهای مورد نیاز در View نیز باشد؛ برای مثال متدی که کار نمایش یا مخفی سازی یک تصویر را با کلیک بر روی دکمهای انجام میدهد.
- متادیتا: متادیتا (یا decorator در اینجا) به AngularJS 2.0 اعلام میکند که این کلاس تعریف شده، صرفا یک کلاس ساده نیست و باید به آن به صورت یک کامپوننت نگاه شود.
در ذیل، کدهای یک کامپوننت نمونهی AngularJS 2.0 را مشاهده میکنید:
import { Component } from 'angular2/core'; @Component({ selector: 'pm-app', template:` <div><h1>{{pageTitle}}</h1> <div>My First Component</div> </div> ` }) export class AppComponent { pageTitle: string = "DNT AngularJS 2.0 APP"; }
بلافاصله در بالای این کلاس، متد decorator ایی را به نام Component مشاهده میکنید. این متادیتا است که به AngularJS 2.0 اعلام میکند، کلاس AppComponent تعریف شده، یک کامپوننت است و نه تنها یک کلاس ساده.
در متد Component تعریف شده، قالب یا template نحوهی رندر این کامپوننت را مشاهده میکنید.
در ابتدای این ماژول نیز کار import تعاریف مرتبط با متد ویژهی Component، از هستهی AngularJS 2.0 انجام شدهاست تا کامپایلر TypeScript بتواند این فایل ts را کامپایل کند.
مروری بر نحوهی تعریف class در TypeScript
مرور جامع کلاسها در TypeScript را در مطلب «مبانی TypeScript؛ کلاسها» میتوانید مطالعه کنید. در اینجا جهت یادآوری، خلاصهای از آنرا که نیاز داریم، بررسی خواهیم کرد:
- جهت تعریف یک کلاس، ابتدا واژهی کلیدی class به همراه نام کلاس ذکر میشوند.
- در AngularJS 2.0 مرسوم است که نام کلاس را به صورت نام ویژگی مدنظر به همراه پسوند Component ذکر کنیم؛ مانند AppComponent مثال فوق. این نام pascal case است و با حروف بزرگ شروع میشود.
- همچنین مرسوم است در برنامههای AngularJS 2.0، کامپوننت ریشهی سایت نیز AppComponent نامیده شود.
- در مثال فوق، واژهی کلیدی export را نیز پیش از واژهی کلیدی class مشاهده میکنید. به این ترتیب این کلاس خارج از ماژولی که در آن تعریف میشود، قابل دسترسی خواهد بود. اکنون این کلاس و فایل، تبدیل به ماژولی خواهند شد که توسط module loader معرفی شدهی در ابتدای بحث یا همان System.JS به صورت خودکار بارگذاری میشود و دیگر نیازی به تعریف مدخل script متناظر با آن در فایل index.html نخواهد بود.
- در بدنهی کلاس، کار تعریف متدها و خواص مورد نیاز View صورت میگیرند. برای نمونه در اینجا تنها یک خاصیت «عنوان صفحه» تعریف شدهاست. در جاوا اسکریپت مرسوم است که نام خواص را camel case شروع شده با حروف کوچک تعریف کنیم. سپس نوع این خاصیت به صورت رشتهای تعریف شدهاست و در آخر مقدار پیش فرض این خاصیت ذکر گردیدهاست.
البته باید دقت داشت که الزامی به ذکر نوع خاصیت، در TypeScript وجود ندارد. همینقدر که مقدار پیش فرض این خاصیت رشتهای است، بر اساس ویژگی به نام Type inference در TypeScript، نوع این خاصیت نیز رشتهای درنظر گرفته خواهد شد و دیگر نمیتوان برای مثال یک عدد را به آن انتساب داد.
- سطح دسترسی خواص تعریف شدهی در یک کلاس TypeScript به صورت پیش فرض public است. بنابراین در اینجا نیازی به ذکر صریح آن نبودهاست.
مروری بر متادیتا یا decorator یک کلاس در AngularJS 2.0
خوب، تا اینجا کلاس AppComponent تعریف و همچنین export شد تا توسط system.js به صورت خودکار بارگذاری شود. اما این کلاس به خودی خود صرفا یک کلاس TypeScript ایی است و توسط AngularJS شناسایی نمیشود. برای معرفی این کلاس به صورت یک کامپوننت، از یک تزئین کننده یا decorator ویژه به نام Component استفاده میشود که بلافاصله در بالای تعریف کلاس قرار میگیرد؛ چیزی شبیه به data annotations یا attributes در زبان #C.
یک decorator متدی است که اطلاعاتی اضافی را به یک کلاس، اعضاء و متدهای آن و یا حتی آرگومانهای آن متدها، الصاق میکند. این ویژگی قرار است به صورت استاندارد در ES 2016 یا نگارش بعدی جاوا اسکریپت حضور داشته باشد و در حال حاضر توسط TypeScript پشتیبانی شده و در نهایت به کدهای ES 5 قابل اجرای در تمام مرورگرها ترجمه میشود.
یک decorator همیشه با @ شروع میشود و AngularJS 2.0 به همراه تعدادی decorator توکار است؛ مانند Component. از آنجائیکه decorator یک متد است، همیشه به همراه یک جفت پرانتز () ذکر میشود و در انتهای آن نیازی به ذکر سمیکالن نیست. در اینجا تزئین کنندهی Component یک شیء را میپذیر که به همراه تعدادی خاصیت است. به همین جهت پارامتر آن به صورت {} ذکر شدهاست.
خاصیت selector یک کامپوننت مشخص میکند که نام directive متناظر با این کامپوننت چیست:
selector: 'pm-app',
<div> @RenderBody() <pm-app>Loading App...</pm-app> </div>
template:` <div><h1>{{pageTitle}}</h1> <div>My First Component</div> </div> `
همچنین {{}} به معنای تعریف data binding است. به این ترتیب مقداری که قرار است به صورت تگ h1 رندر شود، از خاصیت pageTitle کلاس مزین شدهی توسط این ویژگی یا decorator تامین خواهد شد؛ یعنی مقدار پیش فرض خاصیت pageTitle در کلاس AppComponent.
import اطلاعات مورد نیاز جهت کامپایل یک فایل TypeScript
تا اینجا مفاهیم موجود در کلاس AppComponent را به همراه متادیتای آن بررسی کردیم. اما این متادیتای جدید کامپوننت، به صورت پیش فرض ناشناختهاست:
همانطور که مشاهده میکنید، در اینجا ذیل کامپوننت، خط قرمزی جهت یادآوری عدم تعریف آن، کشیده شدهاست. در TypeScript و یا ES 6، پیش از استفاده از یک کلاس یا متد خارجی، نیاز است به module loader اعلام کنیم تا آنرا باید از کجا تامین کند. اینکار توسط عبارت import انجام میشود که شبیه به عبارت using در زبان سیشارپ است. عبارت import جزئی از استاندارد ES 6 است و همچنین در TypeScript نیز پشتیبانی میشود. به این ترتیب امکان دسترسی به ویژگیهای export شدهی از سایر ماژولها را در ماژول فعلی (فایل فعلی در حال کار) خواهیم یافت. نمونهی آنرا با ذکر واژهی کلیدی export پیش از کلاس AppComponent پیشتر ملاحظه کردید.
این ماژولهای خارجی میتوانند سایر ماژولها و فایلهای ts نوشته شدهی توسط خودمان و یا حتی اجزای AngularJS 2.0 باشند. طراحی AngularJS 2.0 نیز ماژولار است و از ماژولهایی مانند angular2/core، angular2/animation، angular2/http و angular2/router تشکیل شدهاست.
برای نمونه متادیتای کامپوننت، در ماژول angular2/core قرار دارد. به همین جهت نیاز است در ابتدای ماژول فعلی آنرا import کرد:
import { Component } from 'angular2/core';
ساخت کامپوننت ریشهی یک برنامهی AngularJS 2.0
در ابتدای بحث که تعاریف مداخل مورد نیاز جهت اجرای یک برنامهی AngularJS 2.0 ذکر شدند، عنوان شد که system.js به دنبال ماژول آغازین app/main میگردد.
بنابراین در ریشهی پروژه، پوشهی جدیدی را به نام app ایجاد کنید.
سپس یک فایل TypeScript جدید را به نام app.component.ts به این پوشه اضافه کنید. قالب این نوع فایلها در add new item و با جستجو typescript در دسترس است و یا حتی یک فایل متنی ساده را هم با پسوند ts ایجاد کنید، کافی است.
نامگذاری این فایلها هم مرسوم است به صورت ذکر نام ویژگی به همراه یک دات و سپس ذکر کامپوننت صورت گیرد. در اینجا چون قصد داریم کامپوننت ریشهی برنامه را ایجاد کنیم، نام ویژگی آن app خواهد بود و نام کامل فایل به این ترتیب app.component.ts میشود.
سپس محتوای این فایل را به دقیقا معادل کدهای قسمت «نوشتن اولین کامپوننت AngularJS 2.0» ابتدای بحث تغییر دهید (همان import، متادیتا و کلاس AppComponent).
تا اینجا کامپوننت ریشهی برنامه ایجاد شد. اما چگونه باید از آن استفاده کنیم و چگونه AngularJS 2.0 آنرا شناسایی میکند؟ به این فرآیند آغازین شناسایی و بارگذاری ماژولها، اصطلاحا bootstrapping میگویند. تنها صفحهی واقعی موجود در یک برنامهی تک صفحهای وب، همان فایل index.html است و سایر صفحات و محتوای آنها به محتوای این صفحهی آغازین اضافه یا کم خواهند شد.
<div> @RenderBody() <pm-app>Loading App...</pm-app> </div>
خوب، اکنون module loader یا system.js چگونه این pm-app یا کامپوننت ریشهی برنامه را شناسایی میکند؟
System.import('app/main')
/// <reference path="../node_modules/angular2/typings/browser.d.ts" /> import { bootstrap } from "angular2/platform/browser"; // Our main component import { AppComponent } from "./app.component"; bootstrap(AppComponent);
در انتهای این فایل متد bootstrap مربوط به AngularJS 2.0 را مشاهده میکنید. کار آن بارگذاری اولین ماژول و کامپوننت برنامه یا همان AppComponent است.
در ادامه نیاز است AppComponent را به این ماژول معرفی کرد؛ در غیراینصورت کامپایل نخواهد شد. برای این منظور سطر import این کلاس را از فایل app.component، مشاهده میکنید. در اینجا نیازی به ذکر پسوند ts. فایل app.component نیست.
سپس نیاز است محل تعریف متد بوت استرپ را نیز مشخص کنیم. این متد در ماژول angular2/platform/browser قرار دارد که به عنوان اولین import این فایل ذکر شدهاست.
سطر اول، مربوط است به تعریف فایلهای d.ts. مربوط به TypeScript جهت شناسایی نوعهای مرتبط با AngularJS 2.0. اگر اینکار صورت نگیرد، خطاهای ذیل را در حین کامپایل فایلهای TypeScript دریافت خواهید کرد:
node_modules\angular2\src\core\application_ref.d.ts(171,81): error TS2304: Build: Cannot find name 'Promise'. node_modules\angular2\src\core\change_detection\differs\default_keyvalue_differ.d.ts(23,15): error TS2304: Build: Cannot find name 'Map'.
تا اینجا پوشهی app واقع در ریشهی سایت، یک چنین شکلی را پیدا میکند:
و اکنون اگر برنامه را اجرا کنیم (فشردن دکمهی F5)، خروجی آن در مرورگر به صورت ذیل خواهد بود:
با توجه به اینکه در حال کار با یک برنامهی جاوا اسکریپتی هستیم، باز نگه داشتن developer tools مرورگر، جهت مشاهدهی خطاهای احتمالی ضروری است.
در اینجا اگر خطایی وجود داشته باشد، یا اطلاعات اضافی مدنظر باشد، در console لاگ خواهند شد. برای مثال در اینجا عنوان شدهاست که برنامه در حالت توسعه در حال اجرا است. بهتر است برای ارائهی نهایی، متد enableProdMode را در فایل index.html فراخوانی کنید.
همچنین در اینجا میتوان HTML نهایی تزریق شدهی به صفحه را بهتر مشاهده کرد:
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: MVC5Angular2.part2.zip
برای اجرای آن، ابتدا به فایل project.json مراجعه کرده و یکبار آنرا ذخیره کنید تا وابستگیهای اسکریپتی پروژه از اینترنت دریافت شوند (این موارد در قسمت قبل مرور شدند). سپس یکبار هم پروژه را Build کنید تا تمام فایلهای ts آن کامپایل شوند و در آخر، اجرای نهایی پروژه.
خلاصهی بحث
یک برنامهی AngularJS 2.0 متشکل است از تعدادی کامپوننت. بنابراین کلاسی را ایجاد خواهیم کرد تا کدهای پشتیبانی کنندهی View این کامپوننت را تولید کند. سپس این کلاس را با متادیتایی مزین کرده و توسط آن تگ سفارشی ویژهی این کامپوننت و تگهای HTML تشکیل دهندهی این کامپوننت را به AngularJS 2.0 معرفی میکنیم. در اینجا در صورت نیاز وابستگیهای تعریف این متادیتا را توسط واژهی کلیدی import دریافت میکنیم. نام این کلاس بهتر است Pascal case بوده و با حروف بزرگ شروع شود و همچنین به صورت نام ویژگی ختم شدهی به کلمهی Component باشد. در اینجا حتما نیاز است این کلاس export شود تا توسط module loader قابل استفاده و بارگذاری گردد. اگر View این کامپوننت نیاز به دریافت اطلاعاتی دارد، این اطلاعات به صورت خواصی در کلاس کامپوننت تعریف میشوند. این خواص تعریف شدهی با سطح دسترسی عمومی، مرسوم است به صورت camel case تعریف شوند و حروف اول آنها کوچک باشد.
public class TestProfile1 : Profile { protected override void Configure() { // این تنظیم سراسری هست و به تمام خواص زمانی اعمال میشود this.CreateMap<DateTime, string>().ConvertUsing(new DateTimeToPersianDateTimeConverter()); this.CreateMap<User, UserViewModel>(); // Other mappings } public override string ProfileName { get { return this.GetType().Name; } } }
public interface IMapFrom<T> { } public interface IHaveCustomMappings { void CreateMappings(IConfiguration configuration); }
public class PersonViewModel : IMapFrom<Person> { public string Name { get; set; } public string LastName { get; set; } }
public class PersonViewModel : IHaveCustomMapping { public string Name { get; set; } // دیگر پراپرتیها public void CreateMappings(IConfiguration configuration) { configuration.CreateMap<ApplicationUser, PersonViewModel>() .ForMember(m => m.Name, opt => opt.MapFrom(u => u.ApplicationUser.UserName)); // دیگر نگاشتها } }
public void Execute() { var types = Assembly.GetExecutingAssembly().GetExportedTypes(); LoadStandardMappings(types); LoadCustomMappings(types); }
متد LoadStandardMappings:
private static void LoadStandardMappings(IEnumerable <Type> types) { var maps = (from t in types from i in t.GetInterfaces() where i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom< >) && !t.IsAbstract && !t.IsInterface select new { Source = i.GetGenericArguments()[0], Destination = t }).ToArray(); foreach(var map in maps) { Mapper.CreateMap(map.Source, map.Destination); } }
- ابتدا تمامی typeهای تعریف شده در پروژه به متد فوق پاس داده خواهند شد.
- برای هر type تمامی اینترفیسهایی که توسط این type پیادهسازی شده باشند را دریافت خواهیم کرد.
- سپس هر type که اینترفیس IMapFrom را پیادهسازی کرده باشد را پردازش میکنیم.
- سپس از نوعهای Abstract و Interface صرفنظر خواهیم کرد.
- انواع مبدا و مقصد را برای AutoMapper فراهم خواهیم کرد.
- در نهایت AutoMapper براساس آنها نگاشت را ایجاد خواهد کرد.
متد LoadCustomMapping:
private static void LoadCustomMappings(IEnumerable <Type> types) { var maps = (from t in types from i in t.GetInterfaces() where typeof(IHaveCustomMappings).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface select(IHaveCustomMappings) Activator.CreateInstance(t)).ToArray(); foreach(var map in maps) { map.CreateMappings(Mapper.Configuration); } }
توضیح کدهای فوق:
Chrome و قابلیت DirectWrite
- گاهی کروم فونتها را صحیح نمایش میدهد و گاهی خیر؛
- در قسمتی از صفحه فونتها درست نمایش داده میشوند و در قسمت دیگر خیر؛
- با رفرش کردن صفحه سالم، فونتها دچار مشکل میشوند و گاهی بالعکس؛
- بعضی اوقات کاراکترهای مربعی نمایش داده میشوند و گاهی کاراکترهای نامفهوم و به هم ریخته.
به هر حال امیدوارم راه حل مناسبی برای این مشکل (با در نظر گرفتن اینکه از فونت استاندارد استفاده شود) پیدا شود...!
پردازش فایلهای XML با استفاده از jQuery
Cross origin requests are only supported for protocol schemes: http, data, chrome-extension, https, chrome-extension-resource.
Access to restricted URI denied.
chrome.storage.local.set chrome.storage.sync.set
ابزارهای زیادی جهت تست کردن API های برنامههای وب موجود است. یکی از معروفترین آنها Fiddler است که ابزاری مستقل جهت دیباگ تحت پروتکل HTTP میباشد. یکی دیگر از این نرم افزارها Swashbuckle است که از Nuget قابل دریافت است و صفحهای را به برنامه اضافه میکند که به اختصار API های برنامه را نمایش داده و امکان اجرای آنها را فراهم میکند.
اما سادهترین راه جهت کردن تست کردن API های یک برنامه، استفاده از PowerShell است که عمل ایجاد درخواستهای HTTP را به راحتی از طریق Command line فراهم میکند و به شما اجازه میدهد بدون وارد شدن به جزئیات، بر روی نتیجه API مورد نظر تمرکز کنید. PowerShell ابتدا فقط برای محیط ویندوز طراحی شده بود ولی در حال حاضر قابلیت اجرای تحت Linux و Mac را نیز دارد.
در این بخش به شما نشان خواهم داد که چگونه متدهای یک Controller را با استفاده از PowerShell تست نمایید.
بدین منظور ابتدا کلاسی را به نام Reservation با مشخصات زیر، در یک پروژه ASP.NET Core Web Application ایجاد نمایید.public class Reservation { public int ReservationId { get; set; } public string ClientName { get; set; } public string Location { get; set; } }
public interface IRepository { IEnumerable<Reservation> Reservations { get; } Reservation this[int id] { get; } Reservation AddReservation(Reservation reservation); Reservation UpdateReservation(Reservation reservation); void DeleteReservation(int id); }
public class MemoryRepository : IRepository { private Dictionary<int, Reservation> items; public MemoryRepository() { items = new Dictionary<int, Reservation>(); new List<Reservation> { new Reservation { ClientName = "Alice", Location = "Board Room" }, new Reservation { ClientName = "Bob", Location = "Lecture Hall" }, new Reservation { ClientName = "Joe", Location = "Meeting Room 1" } }.ForEach(r => AddReservation(r)); } public Reservation this[int id] => items.ContainsKey(id) ? items[id] : null; public IEnumerable<Reservation> Reservations => items.Values; public Reservation AddReservation(Reservation reservation) { if (reservation.ReservationId == 0) { int key = items.Count; while (items.ContainsKey(key)) { key++; }; reservation.ReservationId = key; } items[reservation.ReservationId] = reservation; return reservation; } public void DeleteReservation(int id) => items.Remove(id); public Reservation UpdateReservation(Reservation reservation) => AddReservation(reservation); }
سپس کنترلری را به نام ReservationController به پروژه اضافه کنید که شامل متدهای زیر باشد:
[Route("api/[controller]")] public class ReservationController : Controller { private IRepository repository; public ReservationController(IRepository repo) => repository = repo; [HttpGet] public IEnumerable<Reservation> Get() => repository.Reservations; [HttpGet("{id}")] public Reservation Get(int id) => repository[id]; [HttpPost] public Reservation Post([FromBody] Reservation res) => repository.AddReservation(new Reservation { ClientName = res.ClientName, Location = res.Location }); [HttpPut] public Reservation Put([FromBody] Reservation res) => repository.UpdateReservation(res); [HttpPatch("{id}")] public StatusCodeResult Patch(int id, [FromBody] JsonPatchDocument<Reservation> patch) { Reservation res = Get(id); if (res != null) { patch.ApplyTo(res); return Ok(); } return NotFound(); } [HttpDelete("{id}")] public void Delete(int id) => repository.DeleteReservation(id); }
تست کردن درخواست از نوع GET
حالا دستور زیر را در پنجره PowerShell وارد کنید:Invoke-RestMethod http://localhost:7000/api/reservation -Method GET
location | clientName | reservationId |
Board Room | Alice | 0 |
Lecture Hall | Bob | 1 |
Meeting Room 1 | Joe | 2 |
حال جهت نمایش فقط یک رکورد از اطلاعات فوق، دستور زیر را وارد نمایید:
Invoke-RestMethod http://localhost:7000/api/reservation/1 -Method GET
location | clientName | reservationId |
Lecture Hall | Bob | 1 |
تست درخواست از نوع Post
تمامی متدهای ارائه شده توسط یک API را میتوان به کمک PowerShell تست کرد. اگرچه در این حالت فرمت بعضی از درخواستهای ارسالی ممکن است کمی به هم ریخته به نظر برسد. در اینجا درخواستی جهت اضافه کردن یک رکورد را به لیست Reservation ارسال میکنیم و نتیجه را در پاسخ ارسال شده از سمت سرور خواهیم دید:
Invoke-RestMethod http://localhost:7000/api/reservation -Method POST -Body (@{clientName="Anne"; location="Meeting Room 4"} | ConvertTo-Json) -ContentType "application/json"
location | clientName | reservationId |
Meeting Room 4 | Anne | 1 |
Invoke-RestMethod http://localhost:7000/api/reservation -Method GET
location | clientName | reservationId |
Board Room | Alice | 0 |
Lecture Hall | Bob | 1 |
Meeting Room 1 | Joe | 2 |
Meeting Room 4 | Anne | 3 |
تست درخواست از نوع PUT
درخواست از نوع PUT جهت جایگزینی دادهای موجود در لیست، با دادهی جدید است. بدین منظور کد داخلی شیء مورد نظر (در اینجا ReservationId) باید در URL گنجانده شود و مقادیر فیلدهای کلاس، در متن درخواست. مثالی جهت درخواست اصلاح داده موجود از طریق فرمان PowerShell :
Invoke-RestMethod http://localhost:7000/api/reservation -Method PUT -Body (@{reservationId="1"; clientName="Bob"; location="Media Room"} | ConvertTo-Json) -ContentType "application/json"
location | clientName | reservationId |
Media Room | Bob | 1 |
Invoke-RestMethod http://localhost:7000/api/reservation -Method GET
location | clientName | reservationId |
Board Room | Alice | 0 |
Media Room | Bob | 1 |
Meeting Room 1 | Joe | 2 |
Meeting Room 4 | Anne | 3 |
استفاده از دستور PATCH
این دستور جهت تغییر اطلاعات یک رکورد به کار میرود، ولی استفاده از آن در غالب برنامههای پیاده سازی شده نادیده گرفته میشود که البته چنانچه کاربران برنامههای مذکور به تمامی فیلدهای یک رکورد دسترسی داشته باشند، معقولانه به نظر میرسد. اما در یک برنامه بزرگ و پیچیده ممکن است به دلایلی از جمله مسایل امنیتی، کاربران اجازه دسترسی به تمامی فیلدهای یک رکورد را نداشته باشند و در این حالت نمیتوان از دستور PUT جهت ارسال درخواست استفاده کرد. دستورهای مبتنی بر PATCH گزینشی بوده و میتوان فقط فیلدهای خاصی را که مورد نظر میباشند، با آن تغییر داد.
ASP.NET Core MVC از استاندارد Json PATCH پشتیبانی میکند و این کار اجازه میدهد تا بتوان تغییرات را بطور یکنواختی انجام داد. جهت مشاهده جزئیات این دستور میتوانید به این لینک مراجعه کنید. اما برای مثال فرض کنید میخواهید چنین درخواستی را که به فرمت Json است از طریق HTTP PATCH به سمت سرور ارسال کنید:
[ { "op": "replace", "path": "clientName", "value": "Bob"}, { "op": "replace", "path": "location", "value": "Lecture Hall"} ]
دربسیاری از مواقع فقط از دستور replace درخواست PATCH استفاده میشود و کد داخلی رکورد مورد نظر، جهت تغییر در Url درخواست ارسالی، فرستاده خواهد شد. ASP.NET Core MVC به طور اتوماتیک این دستور را پردازش کرده و آنرا به صورت آبجکتی از نوع <JsonPatchDocument<T به اکشن کنترلر قید شده، پاس خواهد داد که در اینجا نوع T، از نوع کلاس مورد نظر ما میباشد. فرمت دستور ارسالی از طریق Powershell به شکل زیر خواهد بود:
Invoke-RestMethod http://localhost:7000/api/reservation/2 -Method PATCH -Body (@ { op="replace"; path="clientName"; value="Bob"},@{ op="replace"; path="location"; value="Lecture Hall"} | ConvertTo-Json) -ContentType "application/json"
جهت مشاهده تغییر ایجاد شده، دستور زیر را مجددا اجرا نمایید:
Invoke-RestMethod http://localhost:7000/api/reservation -Method GET
location | clientName | reservationId |
Board Room | Alice | 0 |
Media Room | Bob | 1 |
Lecture Hall | Bob | 2 |
Meeting Room 4 | Anne | 3 |
استفاده از دستور Delete
استفاده از دستور Delete هم به شکل زیر خواهد بود:
Invoke-RestMethod http://localhost:7000/api/reservation/2 -Method DELETE
Invoke-RestMethod http://localhost:7000/api/reservation -Method GET
معرفی سرویسهای ارائه شده توسط شرکتهای گوگل، آمازون و مایکروسافت در قالب رایانش ابری - قسمت اول
رایانش ابری مفهوم نسبتا جدیدی در عرصهی فناوری اطلاعات است و در حال گسترش میباشد. به طور خلاصه رایانش ابری به همه چیز اعم از برنامه کاربردی( Application )، سکو ی ( Platform ) توسعه نرم افزار، سخت افزار و زیرساخت، به عنوان سرویس نگاه میکند. زیرساخت های موجود در مراکز داده( Data Center ) به انضمام نرمافزارهایی که در آن قرار دارند، مجموعههایی را تشکیل میدهند که ابر نامیده میشود. به عبارت سادهتر رایانش ابری یعنی استفاده اشتراکی از برنامهها و منابع یک محیط شبکهای برای انجام یک کار، بدون این که مالکیت، مدیریت منابع شبکه و سخت افزار و برنامهها، برای استفاده کننده مهم باشد. در رایانش ابری منابع کامپیوترها، برای انجام یک کار استفاده میشوند و دادههای مربوط به پردازش، در هیچ کدام از کامپیوترهای شخصی ذخیره نمیشوند، بلکه در جای دیگری در داخل همان منابع شبکه، ذخیره میشوند تا در زمان و مکان دیگری قابل دسترسی باشند.
بر همین اساس شرکت های پیشرو در زمینه فناوری اطلاعات به ارائه سرویس هایی تحت عنوان خدمات رایانش ابری پرداخته اند و هدف از این سری مطالب ارائه شده، شرح مختصری بر سرویسهای ارائه شده می باشد. در قسمت اول به معرفی سرویسهای شرکت گوگل پرداخته می شود و در قسمتهای بعدی، سرویسهای شرکتهای مایکروسافت و آمازون معرفی میگردد.
سرویسهای رایانش ابری گوگل، در زیر دو چتر قرار دارند. گروه اول شامل مجموعه گستردهای از برنامههای محبوب گوگل مانند Google Doc ، Google Health ، Google Mail ، Google Earth هستند که با کلیک بر روی گزینه More و Even More که در بالای صفحه اصلی گوگل قرار دارند، میتوان به آنها دسترسی پیدا کرد.
دومین محصول مبتنی بر ابر گوگل، ابزار توسعه PaaS گوگل است. این سکو در سال 2008 برای توسعه برنامههای تحت وب، با استفاده از زیرساخت گوگل به نام موتور Google App معرفی شد. هدف از آن قادر ساختن توسعه دهندگان برای ساخت و استقرار برنامههای وب بدون نگرانی از زیرساختی است که برنامه بر رویش اجرا میشود. برنامههای این موتور، با زبانهای سطح بالا به ویژه جاوا و پایتون و در چارچوب GAE نوشته میشوند. گوگل به منظور گسترش این نوع برنامهها یک سطح رایگان مشخص از سرویس را ارائه میدهد و زمانی که برنامه از سطح مشخصی از بار پردازشی، ذخیرهسازی و پهنای باند شبکه فراتر رفت، آنگاه شارژها بر مبنای میزان استفاده محاسبه میشود.
برنامههای GAE را باید به گونهای نوشت که با زیرساخت گوگل وفق یابند. این مسئله، باعث محدودیت برنامههای قابل اجرا در GAE میگردد و علاوه بر آن، انتقال برنامهها به سکوی GAE و یا انتقال از این سکو به سایر سکوهای موجود دشوار میشود.
از میان سرویس های ابری رایگان ارائه شده از سوی گوگل، به معرفی سرویس آنالیز گوگل بسنده کرده و تمرکز اصلی بر روی سکوی توسعه نرمافزاری این شرکت ( GAE ) میباشد.
Google Analytics
به اختصار GA نامیده میشود و یک ابزار آماری است که تعداد و انواع بازدیدکنندگان وبسایت و نحوه استفاده از وبسایت را اندازهگیری میکند. این محصول بر روی بسته تحلیلی Urchin 5 که گوگل در سال 2006 آن را خریداری نمود، ساخته شده است. این سرویس رایگان عرضه میشود و فرآیند تحلیل را با استفاده از یک قطعه کد جاوا اسکریپت به نام Google Analytics Tracking Code با پیادهسازی در تگ صفحه وب انجام میشود.
این کد با اولین بارگذاری صفحه در سیستم کاربران، به جمع آوری اطلاعات مورد نیاز پرداخته و برای پردازش به سرورهای GA باز پس میفرستد. این کد با کمک Cookie مرورگر اطلاعات مورد نیاز را جمع آوری مینماید.
آشنایی با Google App Engine
GAE یک سکو به عنوان سرویس میباشد و مبتنی بر ابر گوگل است و بر روی زیر ساخت گوگل مستقر شده است.
این سرویس توسعه دهندگان را قادر میسازد تا برنامههای وب ایجاد کرده و بر روی سرورهای گوگل مستقر سازند و گوگل مدیریت زیرساخت را بر عهده گیرد و اعمالی مانند نظارت، برطرف کردن اشکالات احتمالی، خوشه بندی، مدیریت وهلهسازی ماشینهای مجازی و غیره را انجام دهد. برای اجرای یک برنامه در GAE ابتدا باید استانداردهای سکوی گوگل رعایت شود. این استانداردها دامنه برنامههایی که قابل اجرا میباشند را بسیار محدود مینماید و قابلیت حمل آنها را کاهش میدهد.
محدودیتهایی
که این سکو ایجاد میکند، با خود مزایایی را به همراه میآورد که در زیر به آنها
اشاره میگردد:
- وب سرویسهای پویا بر مبنای استانداردهای رایج
- توسعه خودکار و توازن بار بین ماشینهای وهلهسازی شده که
مورد استفاده وب سرویس است.
- اعتبارسنجی با استفاده از API
موجود در گوگل.
- فضای ذخیره سازی ماندگار با قابلیت جستجو، مرتب سازی و
مدیریت تراکنش.
- صف کاری و زمان بندی کاری
- محیط توسعه سمت مشتری( توسعه دهندگان ) برای شبیه سازی GAE
در سیستم محلی.
- پشتیبانی از محیط اجرا جاوا و پایتون.
هنگامی که یک برنامه در GAE مستقر گردید، با استفاده از نام دامنه دلخواه یا با استفاده از آدرس تجاری Google Apps قابل دستیابی است. موتور Google Apps در حال حاضر برنامههایی که در جاوا و پایتون نوشته شده است را پشتیبانی میکند و علاوه بر آن از زبانهای ماشین مجازی جاوا و چندین چارچوب تحت وب پایتون که WSGI و CGI را پشتیبانی میکنند نیز با محیط GAE سازگاری دارند.
برنامههایی که در GAE اجرا میشوند از سیستم عامل مستقل هستند یا به گفته گوگل بر روی Sand Box اجرا میشوند. این ویژگی GAE را قادر میسازد، سیستم را بهینه کند تا تقاضاهای وب، با بار ترافیکی فعلی منطبق شوند. همچنین برنامهها را قادر میسازد با امنیت بالاتری کار کنند، زیرا تنها میتوانند به کامپیوترهایی متصل شوند که آدرسهای مشخصی دارند و سرویسها را با استفاده از پروتکل Http و یا Https از پورتهای شناخته شده پاسخ دهند. از طرف دیگر برنامهها نیز به این میزان محدود شده که تنها فایلها را بخوانند. آنها حق نوشتن فایل به صورت مستقیم بر روی سیستمها را ندارند و برای دستیابی به داده، باید از ذخیره داده در Cache یا سرویس ماندگار دیگری استفاده نمایند.
GAE یک سیستم انبار داده توزیع شده دارد که از پرس و جوها و تراکنشها پشتیبانی مینماید. این انبار داده غیر رابطهای است، اما اشیاء داده یا موجودیتهایی که خصوصیات لازم را دارند، ذخیره مینماید. به همین علت در پرس و جوها میتوان از فیلتر نوع موجودیت بهره برد و آنها را به ترتیب خصوصیات مرتب نمود.
در نهایت توجه به مدل قیمتگذاری گوگل قابل توجه است. گوگل برای تشویق توسعه دهندگان در نوشتن برنامه با استفاده از GAE ، استقرار و توسعه برنامه را تا میزان مشخصی از منابع رایگان کرده است و با عبور از مقدار رایگان باید هزینه را به ازای مصرف پرداخت نمود. بر اساس جدول ارائه شده در سایت شرکت گوگل به ازای تجاوز از میزان مصرف رایگان، سیستم هزینه گذاری بر اساس تعرفههای زیر، اقدام به محاسبه حق شارژ مینماید و بدیهی است برای آگاهی از آخرین تعرفهها و کسب اطلاعات بیشتر، مراجعه به صفحه سایت شرکت گوگل توصیه میشود:
- مبلغ به ازای هر یک ساعت استفاده از CPU
معادل 0.08 دلار
- داده ذخیره شده به ازای هر گیگابایت در ماه
معادل 0.18 دلار
- پهنای باند خروجی به ازای هر گیگابایت معادل
0.12 دلار
- پهنای باند ورودی رایگان
- هزینه دریافت هر ایمیل معادل 0.0001 دلار
به منظور ذخیره اطلاعات در منبع داده پایدار، از API استفاده میگردد که به ازای تعداد تراکنشهایی که تبادل میگردد، هزینه پرداخت میشود. از آنجایی که بنا به تعداد تبادلات و نوع حافظه پایداری که استفاده میگردد، هزینه متغیر است، خواننده محترم برای رویت لیست مذکور به منبع ذکر شده، ارجاع داده میشود.
منبع سهمیه | سهمیه پیش فرض رایگان به ازای هر برنامه |
مصرف CPU | 28 ساعت به ازای هر برنامه در روز |
منبع ذخیره پایدار داده | 1 گیگابایت به ازای هر برنامه در ماه |
پهنای باند ورودی | 1 گیگابایت به ازای هر برنامه در روز |
پهنای باند خروجی | 1 گیگابایت به ازای هر برنامه در روز |
تراکنش با منبع داده Datastore | 50 هزار تراکنش برای خواندن و نوشتن به ازای هر برنامه در ماه |
تراکنش با منبع داده Blobstore | 5 گیگابایت به ازای هر برنامه در روز |
ایمیل دریافتی | 100 دریافت به ازای هر برنامه در روز |