مطالب
ASP.NET Web API - قسمت اول
بخش هایی از کتاب "مرجع کامل ASP.NET MVC (با پوشش کامل ASP.NET MVC 4)"
ترجمه و تالیف: بهروز راد
وضعیت: در دست چاپ


Web API چیست؟

Web API، نوع قالب جدیدی برای پروژه‌های مبتنی بر وب در NET. است که بر مبنای اصول و الگوهای موجود در ASP.NET MVC ساخته شده است و همراه با ASP.NET MVC 4 وجود دارد. Web API توسعه گران را قادر می‌سازد تا با استفاده از یک الگوی ساده که در Controllerها پیاده سازی می‌شود، وب سرویس‌های مبتنی بر پروتوکل HTTP را با کدها و تنظیمات کم ایجاد کنند. این سبک جدید برای ایجاد وب سرویس ها، می‌تواند در انواع پروژه‌های NET. مانند ASP.NET MVC، ASP.NET Web Forms، Windows Application و ... استفاده شود.
یک سوال کاملاً منطقی در اینجا به وجود می‌آید. چرا نیاز به بستری جدید برای ایجاد وب سرویس داریم؟ آیا در حال حاضر مایکروسافت بستری محبوب و فراگیر برای توسعه‌ی وب سرویس هایی که بتوانند با پروتوکل SOAP تعامل داشته باشند در اختیار ندارد؟ مگر وب سرویس‌های ASMX از زمان معرفی ASP.NET وجود نداشته اند؟ آیا تکنولوژی WCF مایکروسافت، بیشترین انعطاف پذیری و قدرت را برای تولید وب سرویس‌ها در اختیار قرار نمی‌دهد؟ وب سرویس‌ها جایگاه خود را یافته اند و توسعه گران با تکنولوژی‌های موجود به خوبی آنها را پیاده سازی و درک می‌کنند. چرا Web API؟

چرا Web Api؟
برای پاسخ به این سوال، باید برخی مشکلات را بررسی کنیم و ببینیم ابزارهای موجود چه راه حلی برای آنها در نظر گرفته اند. اگر با گزینه هایی که در ادامه می‌آیند موافق هستید، خواندن این مطلب را ادامه دهید، و اگر اعتقادی به آنها ندارید، پس نیازهای شما به خوبی با بسترهای موجود پاسخ داده می‌شوند.
  • من معتقد هستم که راه بهتری برای ایجاد وب سرویس‌ها وجود دارد.
  • من معتقد هستم که روش‌های ساده‌تری برای ایجاد وب سرویس‌ها وجود دارد و WCF بیش از حد پیچیده است.
  • من معتقد هستم که تکنولوژی‌های پایه‌ی وب مانند اَفعال GET، POST، PUT و DELETE برای انجام اَعمال مختلف توسط وب سرویس‌ها کافی هستند.
اگر همچنان در حال خواندن این مطلب هستید، توضیحات خود را با شرح تفاوت میان Web API و تکنولوژی‌های دیگر هم حوزه‌ی آن ادامه می‌دهیم و خواهید دید که استفاده از Web API چقدر آسان است.

تفاوت Web API و WCF
وب سرویس‌های ASMX تا چندین سال، انتخاب اول برای ایجاد وب سرویس‌های مبتنی بر پروتوکل SOAP با استفاده از پروتوکل HTTP بودند. وب سرویس‌های ASMX، از وب سرویس‌های ساده که نیاز به قابلیت تعامل پایین داشتند و در نتیجه به پروتوکل SOAP نیز وابسته نبودند پشتیبانی نمی‌کردند. WCF جای وب سرویس‌های ASMX را گرفت و خود را به عنوان آخرین و بهترین روش برای ایجاد وب سرویس‌ها در بستر NET. معرفی کرد. نمونه ای از یک سرویس WCF بر مبنای پروتوکل HTTP در NET. به صورت ذیل است.
[ServiceContract]
public interface IService1                       
{
    [OperationContract]                                 
    string GetData(int value);
    [OperationContract]
    CompositeType GetDataUsingDataContract(CompositeType composite);
}
...
public class Service1 : IService1                         
{
    public string GetData(int value)
    {
        return string.Format("You entered: {0}", value);
    }

    public CompositeType GetDataUsingDataContract(CompositeType composite)
    {
        if (composite == null)
        {
            throw new ArgumentNullException("composite");
        }
        if (composite.BoolValue)
        {
            composite.StringValue += "Suffix";
        }
        return composite;
    }
}
در WCF، پایه و اساس وب سرویس را یک interface تشکیل می‌دهد. در حقیقت اجزای وب سرویس را باید در یک interface تعریف کرد. هر یک از متدهای وب سرویس در interface تعریف شده که صفت OperationContract برای آنها در نظر گرفته شده باشد، به عنوان یکی از اَعمال و متدهای قابل فراخوانی توسط استفاده کننده از وب سرویس در دسترس هستند. سپس کلاسی باید ایجاد کرد که interface ایجاد شده را پیاده سازی می‌کند. در قسمت بعد، با مفاهیم پایه‌ی Web API و برخی کاربردهای آن در محیط ASP.NET MVC آشنا می‌شوید.

نتیجه گیری
Web API، یک روش جدید و آسان برای ایجاد وب سرویس ها، بر مبنای مفاهیم آشنای ASP.NET MVC و پایه‌ی وب است. از این روش می‌توان در انواع پروژه‌های NET. استفاده کرد.
بازخوردهای دوره
شروع به کار با RavenDB
سلام
آقای نصیری موارد کاربردهای Raven.DB چی هست؟
 ممنون
نظرات مطالب
آشنایی با الگوی MVP
سلام
احتمالا 3 tier را شنیده‌اید که به این صورت مطرح کردید.
n-tier نوعی معماری است که به شما در تهیه برنامه‌های توزیع شده کمک می‌کند و مهم‌ترین مزیت آن قابلیت بسط پذیری سیستم است. Tiering‌ در مورد تخصیص منابع و نحوه‌ی توزیع آن‌ها بحث می‌کند. برای مثال دیتابیس سرور شما جدا است، منطق برنامه در سروری دیگر توسط یک وب سرویس قابل دسترسی است و سروری دیگر کار دریافت و ارائه این اطلاعات را به عهده خواهد داشت.
MVC که در ابتدا پدید آمد و بعد از آن MVP ، یک نوع الگوی برنامه نویسی شیء‌گرا هستند که به شما کمک خواهند کرد تا برنامه‌ی n-tier ایی با حداقل گره خوردگی و به هم پیچیدگی که اصطلاحا به آن Loosely coupled نیز گفته می‌شود، تولید کنید.
مطالب
مستند سازی ASP.NET Core 2x API توسط OpenAPI Swagger - قسمت اول - معرفی
مستندسازی یک API، مهم است. اگر این API عمومی باشد، نیاز به مستندات ویژه‌ی آن، امری بدیهی است و اگر عمومی نباشد، باز هم باید دارای مستندات باشد؛ از این جهت که سایر توسعه دهنده‌های یک تیم بتوانند به سادگی از آن استفاده کنند و همچنین نیازی نباشد تا یک سؤال مشخص را بارها پاسخ داد. به علاوه عدم وجود مستندات کافی در مورد یک API سبب خواهد شد تا سایر توسعه دهنده‌ها تصور کنند قابلیتی که مدنظرشان است هنوز پیاده سازی نشده‌است و خودشان شروع به توسعه‌ی آن کنند که سبب اتلاف وقت و هزینه خواهد شد. با استفاده از Swagger که «سوواَگِر» تلفظ می‌شود و یا OpenAI می‌توان این مشکل را برطرف کرد که ارائه دهنده‌ی توضیحی استاندارد از API شما می‌باشد. توسط آن می‌توان قابلیت‌های یک API را دریافت و یا نحوه‌ی تعامل با آن‌را از طریق HTTP، بررسی کرد. چون خروجی آن استاندارد است و مبتنی بر JSON و یا YAML می‌باشد، ابزارهایی نیز برای آن تهیه شده‌اند که برای نمونه می‌توانند بر مبنای آن، یک UI را طراحی و ارائه کنند. البته این ابزارهای ثالث صرفا محدود به تولید UI مستندات مبتنی بر OpenAPI نیستند، بلکه امکان تولید کد و یا آزمون‌های واحد نیز توسط آن‌ها وجود دارد.
به OpenAPI Specification عبارت Swagger Specification نیز گفته می‌شود؛ اما OpenAPI عبارتی است که باید ترجیح داده شود.


OpenAPI و Swagger

تا اینجا دریافتیم که استاندارد OpenAPI و یا Swagger، صرفا دو واژه برای انجام کاری مشابه هستند. اما در عمل Swagger تشکیل شده‌است از تعدادی ابزار سورس باز که برفراز استاندارد OpenAPI کار کرده و قابلیت‌هایی را ارائه می‌دهند؛ مانند Swagger Editor (برای تولید استاندارد)، Swagger UI (برای تولید UI مستندات)، Swagger CodeGen (برای تولید کدهای خودکار) و غیره. برای نمونه swagger-ui را می‌توانید در مخزن کد GitHub آن ملاحظه کنید.
البته این ابزارها به صورت عمومی تهیه شده‌اند. به همین جهت محصور کننده‌هایی نیز برای آن‌ها جهت یکپارچگی با ASP.NET Core، طراحی شده‌اند؛ مانند میان‌افزار Swashbuckle.AspNetCore. کار آن تولید OpenAPI Specification بر اساس ASP.NET Core 2x API شما می‌باشد که جزئیات انجام اینکار را به مرور بررسی خواهیم کرد. این کامپوننت به همراه یک swagger-ui جایگذاری شده (embedded) نیز می‌باشد که کار تهیه‌ی یک UI خودکار را بر اساس این استاندارد میسر می‌کند.
البته محصورکننده‌های متعددی برای ابزارهای Swagger، برای پروژه‌های ASP.NET Core وجود دارند که یکی دیگر از آن‌ها NSwag است. هرچند Swashbuckle.AspNetCore پرکاربردترین مورد تا به امروز بوده‌است.


ساختار نمونه برنامه‌ای که در این سری تکمیل خواهد شد


در اینجا ساختار برنامه‌ی ASP.NET Core 2.2x نمونه‌ی این سری را ملاحظه می‌کنید که هدف اصلی آن، ارائه‌ی دو کنترلر Web API برای کتاب‌ها و نویسنده‌های آن‌ها می‌باشد:


برای نمونه اگر برنامه را اجرا کنید، در مسیر https://localhost:5001/api/authors، لیست تمام نویسندگانی که به صورت اطلاعات آغازین برنامه، توسط OpenAPISwaggerDoc.DataLayer ثبت شده‌اند، قابل مشاهده‌است:


و یا کتاب‌های نویسنده‌ای خاص را در آدرسی مانند https://localhost:5001/api/authors/2902b665-1190-4c70-9915-b9c2d7680450/books می‌توان مشاهده کرد:



کدهای کامل آغازین این نمونه برنامه را از اینجا می‌توانید دریافت کنید: OpenAPISwaggerDoc-01.zip و شامل این اجزا است:
- OpenAPISwaggerDoc.Entities: به همراه موجودیت‌های نویسنده و کتاب است.
- OpenAPISwaggerDoc.DataLayer: شامل DbContext برنامه است؛ به همراه تعدادی رکورد پیش‌فرض جهت آغاز بانک اطلاعاتی و چون DbContext را در یک پروژه‌ی مجزا قرار داده‌ایم، نیاز به IDesignTimeDbContextFactory نیز دارد که این مورد هم در این پروژه لحاظ شده‌است.
- OpenAPISwaggerDoc.Models: شامل DTOهای برنامه است. برای نگاشت این DTOها به موجودیت‌ها و برعکس، از AutoMapper استفاده شده‌است که کار این نگاشت‌ها و تعریف پروفایل متناظر با آن‌ها، در پروژه‌ی OpenAPISwaggerDoc.Profiles صورت می‌گیرد.
- OpenAPISwaggerDoc.Services: کار استفاده‌ی از لایه‌ی OpenAPISwaggerDoc.DataLayer را جهت دسترسی به بانک اطلاعاتی و کار با موجودیت‌های کتاب‌ها و نویسندگان، انجام می‌دهد. از این سرویس‌ها در دو کنترلر Web API برنامه، برای دریافت اطلاعات نویسندگان و کتاب‌های آن‌ها از بانک اطلاعاتی، استفاده می‌شود.
- OpenAPISwaggerDoc.Web: پروژه‌ی اصلی است که دو کنترلر Web API را هاست می‌کند و تنظیمات اولیه‌ی این سرویس‌ها را در کلاس Startup انجام داده و همچنین کار اعمال خودکار Migrations را نیز در کلاس Program (بالاترین سطح برنامه) تکمیل می‌کند. رشته‌ی اتصالی این برنامه، در فایل appsettings.json تعریف شده‌است و به یک بانک اطلاعاتی LocalDB اشاره می‌کند که پس از اجرای برنامه به صورت خودکار ساخته شده و مقدار دهی می‌شود.


در قسمت بعد، کار مستند سازی این API را شروع می‌کنیم.
مطالب
آپلود فایل ها با استفاده از PlUpload در Asp.Net Mvc
امروزه بازار برنامه‌های تماما ajax و بدون Postback  شدن صفحه بسیار داغ میباشد که از این موارد میتوان به برنامه‌های تحت وب گوگل اشاره کرد. (gmail  ، googlePlus  ، Google Reader)
در این میان یکی از دغدغه‌های توسعه دهندگان وب ، آپلود فایل‌ها به صورت آنی (مثل attach files گوگل) میباشد. برای حل این مسئله ، ابزارها و پلاگین‌های متعددی وجود دارد که در اینجا به 10 تا از پلاگین‌های Jquery  اشاره شده است.
به شخصه با پلاگین Uploadify کار کرده ام و از استفاده از آن راضی هستم ولی همین دیشب برای قسمتی از یک پروژه نیاز
به ابزاری جهت آپلود فایل‌ها با امکانات مورد نظرم داشتم که به PlUpload برخورد کردم. 

از امکاناتی که این ابزار در اختیار شما قرار میدهد :
- یک اینترفیس زیبا جهت آپلود و افزودن فایل ها
- پشتیبانی از زبان‌های مختلف و همین طور زبان فارسی
- امکان استفاده از قالب Jquery UI
- Drag&Drop  برای مرورگرهایی که از Html5  پشتیبانی میکنند

حال که با امکانات این ابزار بیشتر آشنا شدید بریم سراغ استفاده از این ابزار در asp.net mvc  :)
ابتدا پروژه را از اینجا دانلود کنید. سپس یک پروژه‌ی جدید  mvc 3  بسازید (از نوع Internet Application و با نام دلخواه). سپس پوشه‌ی plupload  را در قسمت سلوشن برنامه کپی کنید.
حال در فایل Views->Shared->_Layout.cshtml  ، تگ head  را جهت افزودن امکانات پلاگین این گونه تغییر دهید :

    <title>@ViewBag.Title</title>

    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <link href="../../plupload/js/jquery.plupload.queue/css/jquery.plupload.queue.css" rel="stylesheet" />
    <script src="@Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
    <script type="text/javascript" src="http://bp.yahooapis.com/2.4.21/browserplus-min.js"></script>

    <script src="../../plupload/js/plupload.full.js"></script>
    <script src="../../plupload/js/jquery.plupload.queue/jquery.plupload.queue.js"></script>
    <script src="../../plupload/js/i18n/fa.js"></script>

نکته : فایل fa.js  که جهت استفاده از زبان فارسی در اینترفیس آپلود فایل‌ها میباشد، که وجود آن در آدرس واضح میباشد.
سپس به فایل Views->Home->Index.cshtml بروید و آن را این گونه دوباره نویسی کنید :
 @{
    ViewBag.Title = "Uploading Files using PlUpload";
}
<h2>@ViewBag.Message</h2>

@using (Html.BeginForm("Post", "home", FormMethod.Post,
    new { enctype = "multipart/form-data" }))
{
    <div id="uploader">
        <p>You browser doesn't have Flash, Silverlight, Gears, BrowserPlus or HTML5 support.</p>
    </div>
}

<script>
    $(function () {

        $("#uploader").pluploadQueue({
            // General settings
            runtimes: 'html5,gears,flash,silverlight,browserplus,html4',
            url: '@Url.Action("Upload" , "Home")',
            max_file_size: '10mb',
            chunk_size: '1mb',
            unique_names: true,

            // Resize images on clientside if we can
            resize: { width: 320, height: 240, quality: 90 },

            // Specify what files to browse for
            filters: [
                { title: "Image files", extensions: "jpg,gif,png" },
                { title: "Zip files", extensions: "zip" }
            ],

            // Flash settings
            flash_swf_url: '/plupload/js/plupload.flash.swf',

            // Silverlight settings
            silverlight_xap_url: '/plupload/js/plupload.silverlight.xap'
        });
    });
</script>
توضیحات و نکات :
- جهت آپلود فایل‌ها تگ enctype = "multipart/form-data" را فراموش نکنید.
- در قسمت مقداردهی به ویژگی‌های Plupload  ، قسمت runtime  به صورت ترتیبی کار میکند لذا اگر اولی پشتیبانی نشود سراغ دومی میرود و اگر دومی نشود سومی و ... در صفحه‌ی اول سایت PlUpload ، موارد پشتیبانی شده توسط تکنولوژی‌ها آورده شده است لذا این ترتیب را ترتیب مناسبی میبینم و اگر اولین مورد html5 باشد امکان Drag&Drop وجود خواهد داشت.
خود سایت PlUpload  داکیومنت خیلی خوبی جهت توضیح موارد مختلف دارد لذا توضیح دوباره لازم نیست.
همان طور که در ویژگی url  مشاهده میکنید به کنترلر Home  و اکشن متود Upload اشاره شده است که طرز کار به این گونه است که هر بار که یک فایل آپلود میشود درخواستی به این آدرس و محتوای فایل در قسمت Request.Files ارسال میشود و همین طور نام فایل که unique ارسال میشود و chunk که تیکه‌های فایل است(پست میشود).
پس اکشنی با نام Upload  در کنترلر HomeController بسازید :
        [HttpPost]
        public ActionResult Upload(int? chunk, string name)
        {
            var fileUpload = Request.Files[0];
            var uploadPath = Server.MapPath("~/App_Data");
            chunk = chunk ?? 0;
            using (var fs = new FileStream(Path.Combine(uploadPath, name), chunk == 0 ? FileMode.Create : FileMode.Append))
            {
                if (fileUpload != null)
                {
                    var buffer = new byte[fileUpload.InputStream.Length];
                    fileUpload.InputStream.Read(buffer, 0, buffer.Length);
                    fs.Write(buffer, 0, buffer.Length);
                }
            }
            return Content("chunk uploaded", "text/plain");
        } 
توضیحات : ابتدا فایل مورد نظر از قسمت Request.Files واکشی میشود و سپس فایل را در پوشه App_Data ذخیره میکند. (یکی از چندین روش ذخیره سازی که مطالعه در این قسمت به خواننده واگذار میشود.)

حال برنامه را اجرا کنید و از این ابزار لذت ببرید:) 
نکته : قسمت فارسی ساز اونو تغییر دادم چون که ترجمه‌ی فارسی خودش یه سری نقایصی داشت که گویا از کار با google translate به وجود اومده بود!
نظرات مطالب
صفحه بندی و مرتب سازی خودکار اطلاعات به کمک jqGrid در ASP.NET MVC
خیلی مچکرم؛ من می‌خواستم بدونم آیا می‌تونم بدون فرستادن لیست ستون‌های پنهان شده به یک اکشن دیگر و در نتیچه ذخیره کردن آن بر روی کوکی و یا دیتابیس و یا روش‌های دیگر مستقیم در اکشن GetProducts بگیرتش، اگر بخوام شفاف‌تر بگم ، من می‌خوام وقتی کاربر دکمه خروجی پی دی اف رو زد ستون‌های گرید رو کاربر قبلش تنظیم کرده باشه و در پی دی اف مورد نظر ستون‌های حذف شده رو دیگر نبینه همچنین با در نظر گرفتن موارد فیلتر شده در گرید .
مطالب
عبارت using و نحوه استفاده صحیح از آن
مثال ساده زیر را که در مورد تعریف یک کلاس Disposable و سپس استفاده از آن توسط عبارت using است را به همراه سه استثنایی که در این متدها تعریف شده است، در نظر بگیرید:
using System;

namespace TestUsing
{
    public class MyResource : IDisposable
    {
        public void DoWork()
        {
            throw new ArgumentException("A");
        }

        public void Dispose()
        {
            throw new ArgumentException("B");
        }
    }

    public static class TestClass
    {
        public static void Test()
        {
            using (MyResource r = new MyResource())
            {
                throw new ArgumentException("C");
                r.DoWork();
            }
        }
    }
}
به نظر شما قطعه کد زیر چه عبارتی را نمایش می‌دهد؟ C یا B یا A؟
try
{
     TestClass.Test();
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

پاسخ: برخلاف تصور (که احتمالا C است؛ چون قبل از فراخوانی متد DoWork سبب بروز استثناء شده است)، فقط B را در خروجی مشاهده خواهیم کرد!
و این دقیقا مشکلی است که در حین کار با کتابخانه iTextSharp برای اولین بار با آن مواجه شدم. روش استفاده متداول از iTextSharp به نحو زیر است:
using (var pdfDoc = new Document(PageSize.A4))  
{  
   //todo: ...
}
در این بین هر استثنایی رخ دهد، در لاگ‌های خطای سیستم شما تنها خطاهای مرتبط با خود iTextSharp را مشاهده خواهید کرد و نه مشکل اصلی را که در کدهای ما وجود داشته است. البته این یک مشکل عمومی است و اگر «using statement and suppressed exceptions» را در گوگل جستجو کنید به نتایج مشابه زیادی خواهید رسید.
و خلاصه نتایج هم این است:
اگر به ثبت جزئیات خطاهای سیستم اهمیت می‌دهید (یکی از مهم‌ترین مزیت‌های دات نت نسبت به بسیاری از فریم ورک‌های مشابه که حداکثر خطای 0xABC12EF را نمایش می‌دهند)، از using استفاده نکنید! using در پشت صحنه به try/finally ترجمه می‌شود و بهتر است این مورد را دستی نوشت تا اینکه کامپایلر اینکار را به صورت خودکار انجام دهد.
در اینجا باز هم به یک سری کد تکراری try/finally خواهیم رسید و همانطور که در مباحث کاربردهای Action و Func در این سایت ذکر شد، می‌توان آن‌را تبدیل به کدهایی با قابلیت استفاده مجدد کرد. یک نمونه از پیاده سازی آن‌را در این سایت «C# Using Blocks can Swallow Exceptions » می‌توانید مشاهده کنید که خلاصه آن کلاس زیر است:
using System;

namespace Guard
{
    public static class SafeUsing
    {
        public static void SafeUsingBlock<TDisposable>(this TDisposable disposable, Action<TDisposable> action)
            where TDisposable : IDisposable
        {
            disposable.SafeUsingBlock(action, d => d);
        }

        internal static void SafeUsingBlock<TDisposable, T>(this TDisposable disposable, Action<T> action, Func<TDisposable, T> unwrapper)
            where TDisposable : IDisposable
        {
            try
            {
                action(unwrapper(disposable));
            }
            catch (Exception actionException)
            {
                try
                {
                    disposable.Dispose();
                }
                catch (Exception disposeException)
                {
                    throw new AggregateException(actionException, disposeException);
                }

                throw;
            }

            disposable.Dispose();
        }
    }
}
برای استفاده از کلاس فوق مثلا در حالت بکارگیری iTextSharp خواهیم داشت:
new Document(PageSize.A4).SafeUsingBlock(pdfDoc =>
{
  //todo: ...
});
علاوه بر اینکه SafeUsingBlock یک سری از اعمال تکراری را کپسوله می‌کند، از AggregateException نیز استفاده کرده است (معرفی شده در دات نت 4). به این صورت چندین استثنای رخ داده نیز در سطحی بالاتر قابل دریافت و بررسی خواهند بود و استثنایی در این بین از دست نخواهد رفت.
مطالب
OutputCache در ASP.NET MVC
مقدمه

OutputCaching باعث می‌شود خروجیِ یک اکشن متد در حافظه نگهداری شود. با اعمال این نوع کشینگ، ASP.NET در خواست‌های بعدی به این اکشن را تنها با بازگرداندن همان مقدار قبلی ِ نگهداری شده در کش، پاسخ می‌دهد. در حقیقت با OutputCaching از تکرار چند باره کد درون یک اکشن در فراخوانی‌های مختلف جلوگیری کرده‌ایم. کش کردن باعث می‌شود که کارایی و سرعت سایت افزایش یابد؛ اما باید دقت کنیم که چه موقع و چرا از کَش کردن استفاده میکنیم و چه موقع باید از این کار امتناع کرد. 


فواید کَش کردن

- انجام عملیات هزینه دار فقط یکبار صورت میگیرد. (هزینه از لحاظ فشار روی حافظه سرور و کاهش سرعت بالا آمدن سایت)

- بار روی سرور در زمان‌های پیک کاهش می‌یابد.

- سرعت بالا آمدن سایت بیشتر میشود. 


چه زمانی باید کَش کرد؟ 

- وقتی محتوای نمایشی برای همه کاربران یکسان است.

- وقتی محتوای نمایشی برای نمایش داده شدن، فشار زیادی روی سرور تحمیل میکند.

- وقتی محتوای نمایشی به شکل مکرر در طول روز باید نمایش داده شود.

- وقتی محتوای نمایشی به طور مکرر آپدیت نمی‌شود. (در مورد تعریف کیفیت "مکرر"، برنامه نویس بهترین تصمیم گیرنده است) 


طرح مساله 

فرض کنید صفحه اول سایت شما دارای بخش‌های زیر است :

خلاصه اخبار بخش علمی، خلاصه اخبار بخش فرهنگی ، ده کامنت آخر، لیستی از کتگوری‌های موجود در سایت.

روش‌های مختلفی برای کوئری گرفتن وجود دارد، به عنوان مثال ما به کمک یک یا چند کوئری و توسط یک ViewModel جامع، میخواهیم اطلاعات را به سمت ویو ارسال کنیم. پس در اکشن متد Index ، حجم تقریبا کمی از اطلاعات را باید به کمک کوئری(کوئری های) تقریبا پیچیده ای دریافت کنیم و اینکار به ازای هر ریکوئست هزینه دارد و فشار به سرور وارد خواهد شد. از طرفی میدانیم صفحه اول ممکن است در طول یک یا چند روز تغییر نکند و همچنین شاید در طول یکساعت چند بار تغییر کند! به هر حال در جایی از سایت قرار داریم که کوئری (کوئری های) مورد نظر زیاد صدا زده میشوند ، در حقیقت صفحه اول احتمالا بیشترین فشار ترافیکی را در بین صفحات ما دارد، البته این فقط یک احتمال است و ما دقیقا از این موضوع اطلاع نداریم.

یکی از راه‌های انجام یک کش موفق و دانستن لزوم کش کردن، این است که دقیقا بدانیم ترافیک سایت روی چه صفحه ای بیشتر است. در واقع باید بدانیم در کدام صفحه "هزینه‌ی اجرای عملیات موجود در کد" بیشترین است.

فشار ترافیکی(ریکوئست‌های زیاد) و آپدیت‌های روزانه‌ی دیتابیس را، در دو کفه ترازو قرار دهید؛ چه کار باید کرد؟ این تصمیمی است که شما باید بگیرید. نگرانی خود را در زمینه آپدیت‌های روزانه و ساعتی کمتر کنید؛ در ادامه راهی را معرفی میکنیم که آپدیت‌های هر از گاهِ شما، در پاسخِ ریکوئست‌ها دیده شوند. کمی کفه‌ی کش کردن را سنگین کنید.

به هر حال، فعال کردن قابلیت کش کردن برای یک اکشن، بسیار ساده است، کافیست ویژگی ( attribute ) آن را بالای اکشن بنویسید :

[OutputCache(Duration = "60", Location = OutputCacheLocation.Server)]
        public ActionResult Index()
        {
            //کوئری یا کوئری‌های لازم برای استفاده در صفحه اصلی و تبدیل آن به یک ویو مدل جامع
        }
[OutputCache(CacheProfile = "FirstPageIndex",Location=OutputCacheLocation.Server)]
        public ActionResult Index()
        {
            //کوئری یا کوئری‌های لازم برای استفاده در صفحه اصلی و تبدیل آن به یک ویو مدل جامع
        }

دو روش فوق برای کش کردن خروجی Index  از لحاظ عملکرد یکسان است، به شرطی که در حالت دوم در وب کانفیگ و در بخش system.web آن ، یک پروفایل ایجاد کنیم کنیم :

    <caching>
      <outputCacheSettings>
        <outputCacheProfiles>
          <add name="FirstPageIndex" duration="60"/>
        </outputCacheProfiles>
      </outputCacheSettings>
    </caching>

در حالت دوم ما یک پروفایل برای کشینگ ساخته ایم و در ویژگی بالای اکشن متد، آن پروفایل را صدا زده ایم. از لحاظ منطقی در حالت دوم، چون امکان استفاده مکرر از یک پروفایل در جاهای مختلف فراهم شده، روش بهتری است. محل ذخیره کش نیز در هر دو حالت سرور تعریف شده است.

برای تست عملیات کشینگ، کافیست یک BreakPoint  درون Index قرار دهید و برنامه را اجرا کنید. پس از اجرا، برنامه روی Break Point می‌ایستد و اگر F5 را بزنیم، سایت بالا می‌آید. بار دیگر صفحه را رفرش کنیم، اگر این "بار دیگر" در کمتر از 60 ثانیه پس از رفرش قبلی اتفاق افتاده باشد برنامه روی Break Point متوقف نخواهد شد، چون خروجی اکشن، در کش بر روی سرور ذخیره شده است و این یعنی ما فشار کمتری به سرور تحمیل کرده ایم، صفحه با سرعت بالاتری در دسترس خواهد بود.

ما از تکرار اجرای کد جلوگیری کرده ایم و عدم اجرای کد بهترین نوع بهینه سازی برای یک سایت است. [اسکات الن، پلورال سایت]


چطور زمان مناسب برای کش کردن یک اکشن را انتخاب کنیم؟

- کشینگ با زمان کوتاه؛ فرض کنید زمان کش را روی 1 ثانیه تنظیم کرده اید. این یعنی اگر ریکوئست هایی به یک اکشن ارسال شود و همه در طول یک ثانیه اتفاق بیفتد، آن اکشن فقط برای بار اول اجرا میشود، و در بارهای بعد(در طول یک ثانیه) فقط محتوای ذخیره شده در آن یک اجرا، بدون اجرای جدید، نمایش داده میشود. پس سرور شما فقط به یک ریکوئست در ثانیه در طول روز جواب خواهد داد و ریکوئست‌های تقریبا همزمان دیگر، در طول همان ثانیه، از نتایج آن ریکوئست (اگر موجود باشد) استفاده خواهند کرد

- کشینگ با زمان طولانی؛ ما در حقیقت با اینکار منابع سرور را حفاظت میکنیم، چون عملیاتِ هزینه دار(مثل کوئری‌های حجیم) تنها یکبار در طول زمان کشینگ اجرا خواهند شد. مثلا اگر تنظیم زمان روی عدد 86400 تنظیم شود(یک روز کامل)، پس از اولین ریکوئست به اکشن مورد نظر، تا 24 ساعت بعد، این اکشن اجرا نخواهد شد و فقط خروجی آن نمایش داده خواهد شد. آیا دلیلی دارد که یک کوئری هزینه دار را که قرار نیست خروجی اش در طول روز تغییر کند به ازای هر ریکوئست یک بار اجرا کنیم؟   

اگر اطلاعات موجود در دیتابیس را تغییر دهیم چه کار کنیم که کشینگ رفرش شود؟ 

فرض کنید در همان مثال ابتدای این مقاله، شما یک پست به دیتابیس اضافه کرده اید، اما چون مثلا duration مربوط به کشینگ را روی 86400 تعریف کرده اید تا 24 ساعت از زمان ریکوئست اولیه نگذرد، سایت آپدیت نخواهد شد و محتوا همان چیزهای قبلی باقی خواهند ماند. اما چاره چیست؟

کافیست در بخش ادمین، وقتی که یک پست ایجاد میکنید یا پستی را ویرایش میکند در اکشن‌های مرتبط با Create یا Edit یا Delete چنین کدی را پس از فرمان ذخیره تغییرات در دیتابیس، بنویسید: 

Response.RemoveOutputCacheItem(Url.Action("index", "home"));

واضح است که ما داریم کشینگ مرتبط با یک اکشن متد مشخص را پاک میکنیم. با اینکار در اولین ریکوئست پس از تغییرات اعمال شده در دیتابیس، ASP.NET MVC چون میبیند کَشی برای این اکشن وجود ندارد، متد را اجرا میکند و کوئری‌های درونش را خواهد دید و اولین ریکوئست پیش از کَش شدن را انجام خواهد داد. با اینکار کشینگ ریست شده است و پس از این ریکوئست و استخراج اطلاعات جدید، زمان کشینگ صفر شده و آغاز میشود.

میتوانید یک دکمه در بخش ادمین سایت طراحی کنید که هر موقع دلتان خواست کلیه کش‌ها را به روش فوق پاک کنید! تا اپلیکیشن منتظر ریکوئست‌های جدید بماند و کش‌ها دوباره ایجاد شوند. 


جمع بندی

ویژگی OutputCatch دارای پارامترهای زیادیست و در این مقاله فقط به توضیح عملکرد این اتریبیوت اکتفا شده است. بطور کلی این مبحث ظاهر ساده ای دارد، ولی نحوه استفاده از کشینگ کاملا وابسته به هوش برنامه نویس است و پیچیدگی‌های مرتبط با خود را دارد. در واقع خیلی مشکل است که بتوانید یک زمان مناسب برای کش کردن تعیین کنید. باید برنامه خود را در یک محیط شبیه سازی تحت بار قرار دهید و به کمک اندازه گیری و محاسبه به یک قضاوت درست از میزان زمان کش دست پیدا کنید. گاهی متوجه خواهید شد، از مقدار زیادی از حافظه سیستم برای کش کردن استفاده کرده اید و در حقیقت آنقدر ریکوئست ندارید که احتیاج به این هزینه کردن باشد.

یکی از روش‌های موثر برای دستیابی به زمان بهینه برای کش کردن استفاده از CacheProfile درون وب کانفیگ است. وقتی از کشینگ استفاده میکنید، در همان ابتدا مقدار زمانی مشخص برای آن در نظر نگرفته اید(در حقیقت مقدار زمان مشخصی نمیدانید) پس مجبور به آزمون و خطا و تست و اندازه گیری هستید تا بدانید چه مقدار زمانی را برای چه پروفایلی قرار دهید. مثلا پروفایل هایی به شکل زیر تعریف کرده اید و نام آنها را به اکشن‌های مختلف نسبت داده اید. به راحتی میتوانید از طریق دستکاری وب کانفیگ مقادیر آن را تغییر دهید تا به حالت بهینه برسید، بدون آنکه کد خود را دستکاری کنید. 

<caching>
      <outputCacheSettings>
        <outputCacheProfiles>
          <add name="Long" duration="86400"/>
          <add name="Average" duration="43600"/>
          <add name="Short" duration="600"/>
        </outputCacheProfiles>
      </outputCacheSettings>
    </caching>

برای مطالعه جزئیات بیشتر در مورد OutputCaching مقالات زیر منابع مناسبی هستند.

[اینجا ] و [اینجا ]

مطالب
نکاتی در مورد نوشتن یک مطلب خوب و گیرا در یک سایت
تفاوتی نمی‌کند در چه رشته‌ای یا حرفه‌ای مشغول به کار هستید؛ تفاوتی نمی‌کند در چه زمینه‌ای قصد دارید مطلبی را منتشر کنید. برای تهیه یک مطلب خوب و ماندگار، باید یک سری اصول کلی را در نوشتن  رعایت کرد که در ادامه به مرور آن‌ها خواهیم پرداخت.

1) مطلب شما نیاز به مقدمه دارد
نیاز به مقدمه داشتن به معنای نوشتن کلمه «مقدمه» در ابتدای یک متن نیست. به این معنا است که به خواننده بگوئید مشکل کجا بوده یا به چه دلیلی قصد دارید مطلب جاری را منتشر کنید. بنابراین جهت تهیه یک مطلب خوب، یک راست اصل مطلب را شروع نکنید. لازم است چند سطری در مورد علت انتشار آن توضیح دهید.

2) پیش از انتشار مطلب چندبار آن‌‌را مطالعه کنید
یک مطلب خوب نیاز به جمله بندی مناسب، استفاده از نقطه، ویرگول و امثال آن دارد و بدون استفاده از آن‌ها متن شما هرچقدر هم حرفه‌ای باشد، خواندنش مشکل خواهد بود و جلوه مناسبی نخواهد داشت.

3) سعی کنید در عنوان مطلب خود از کلمات کلیدی استفاده کنید
استفاده از کلمات کلیدی در عنوان مطالب، جستجوی آن‌ها را برای خواننده ساده‌تر کرده و همچنین کمک بزرگی است به موتورهای جستجو در یافتن نتایجی بهتر.

4) تکرار کلمات و جملات یکسان را در متن خود به حداقل برسانید
برای مثال مدام در متن خود جمله «همانطور که ملاحظه می‌کنید» را تکرار نکنید. استفاده از افعال تکراری و جملات تکراری در یک متن باید حداقل باشند. برای نمونه اگر جمله جاری به «می‌شود» ختم خواهد شد، جمله بعدی را به «می‌گردد» ختم کنید. اگر جمله‌ای دارای کلمات «برای مثال» است، جمله بعدی بهتر است به همراه کلمات «برای نمونه» باشد.

5) از جملات طولانی استفاده نکنید
یک جمله باید حداکثر یک سطر یا یک سطر و نصفی طول داشته باشد و گرنه خواننده را به شدت در دنبال کردن آن به زحمت خواهید انداخت. جملات طولانی را به جملاتی کوتاه‌تر خرد کنید.

6) استفاده از علامت تعجب را به حداقل برسانید
اشخاصی که مدام از چندین علامت تعجب پشت سرهم استفاده می‌کنند یا مدام از علامت سؤال به همراه چندین علامت تعجب بهره می‌گیرند، حس مسخره کردن شخص مقابل و همچنین عدم تعادل روانی خود را القاء می‌کنند.

7) در متن خود از تصاویر استفاده کنید
انسان موجودی است بصری. قدرت یادگیری ما از طریق دیدن چند برابر زمانی است که از طریق شنیدن یا خواندن نسبت به فراگیری مطلبی اقدام می‌کنیم.
« ما ...
10 درصد چیزهایی را که می‌خوانیم
20 درصد چیزهایی را که می‌شنویم
30 درصد چیزهایی را که می‌بینیم
50 درصد چیزهایی را که می‌بینیم و می‌شنویم
70 درصد چیزهایی را که در موردشان بحث می‌کنیم
80 درصد چیزهایی را که تجربه می‌کنیم
95 درصد چیزهایی را که به دیگران می‌آموزیم
... یاد می‌گیریم
»

8) اگر در سایتی مطلب می‌نویسید، اهداف کلی آن‌را حفظ کنید
اگر نام سایت شما برنامه نویسی است، خواننده به دنبال شعر، داستان و یا مطالب خیلی شخصی و خصوصی شما نمی‌گردد. سایت‌های زیادی هستند که به این مقوله‌ها می‌پردازند و خیلی زود سطح کاری خود را به این ترتیب به حداقل تنزل خواهید داد.

9) به صفحات داخلی سایت خود لینک دهید
در مطلب تهیه  شده سعی کنید به مطالب مشابه داخلی سایت خود لینک دهید. احتمال کپی شدن مطالب شما بسیار زیاد است. به این ترتیب می‌توانید خواننده‌ها را در لابلای متن خود به مرجع اصلی هدایت کنید.

10) دست به اختراع برچسب‌های جدید، پراکنده و بیهوده نزنید
اگر گروه بندی مطالب یک سایت بر اساس برچسب‌ها است و تاکنون برچسب‌های متعددی تعریف شده است، بهتر است از همان‌ها استفاده کنید تا اینکه دست به اختراع زده و یک برچسب کاملا جدید را ثبت کنید. برای مثال اگر مطلب شما در مورد Entity framework است و تا کنون 20 مطلب ذیل این گروه ثبت شده، اختراع برچسب جدید EF Code first نه تنها کمکی نخواهد کرد، بلکه خواننده‌ای را که به دنبال یافتن مطالب یک گروه خاص است، سر در گم می‌کند. یا اگر قصد دارید یک برچسب کاملا جدید را اضافه کنید، حتما از یک برچسب کلی موجود نیز استفاده کنید تا روابط بین مطالب حفظ شوند.

11) مطالب شما بهتر است یک قسمت نتیجه گیری نیز داشته باشد
بهتر است در پایان یک مطلب، خلاصه بحث، پیشنهادها یا حتی سؤالاتی را مطرح کنید تا بتوانید خواننده را تا حدودی وادار به عکس العمل نمائید.

12) تا حد امکان از منابع سایت خود استفاده کنید
اگر قرار  است تصویری در متن قرار گیرد، اگر نیاز است فایلی در سایت مطرح شود، بهتر است این موارد در سایت جاری هاست شوند؛ تا اینکه تصویر یک سایت دیگر را مستقیما در سایت خود لینک کنیم.

13) معرفی منابع
نیازی نیست در یک سایت، همانند مقالات علمی، در انتهای مطلب لیست منابع را ذکر نمود. در اینجا می‌شود قسمتی از جملات را به منبع استفاده شده لینک کرد. به این ترتیب دقیقا مشخص می‌شود هر قسمت و هر جمله‌ای از چه ماخذی استفاده کرده و پیگیری آن ساده‌تر می‌شود.

14) تصاویر ارائه شده را فشرده کنید
فرمت مناسب ارائه تصویر در یک سایت bmp نیست. بهترین فرمت برای سایت‌ها png است؛ از لحاظ حفظ تعداد رنگ و همچنین کاهش حجم. به علاوه ابزارهای زیادی برای کاهش حجم فایل‌های png با حداقل افت کیفیت وجود دارند.

15) در مورد کدهای خود توضیح دهید
این مورد خصوصا به سایت‌های برنامه نویسی مرتبط می‌شود. اینکه چند سطر کد بدون توضیح را در یک مطلب قرار داده‌اید، نه لطفی است و نه اهمیتی دارد! هزاران هزار سطر از این دست کدها در GitHub، CodPlex و sourceforge وجود دارند. فرق کار شما با آن‌ها در چیست؟
باید یک قسمت «توضیحات» ذیل کدهای ارائه شده وجود داشته باشد. همان نکاتی را که حین تهیه کدها در ذهن داشتید باید بتوانید توضیح دهید و گرنه ... کار شما ارزشی نخواهد داشت.

16) مطالب تجربی شما باید قابلیت تولید مجدد داشته باشند
برای مثال امروز در حین کار به یک مطلب جدید برخورد‌ه‌اید که قصد دارید آن‌را به اشتراک بگذارید. ذکر این نکته جدید به تنهایی کافی نیست. باید ابتدا بتوانید ذهن خواننده را جهت رسیدن به وضعیتی که قرار داشتید، آماده کنید. اگر قصد دارید قطعه کدی را به اشتراک بگذارید، خواننده باید بتواند این قطعه از پازل را در کنار قطعات دیگری که برای او ترسیم خواهید کرد، جای دهد. یا حداقل بتواند قطعه کد شما را بدون خطا کامپایل کند.

17) یک راست به سراغ کدنویسی و اصل مطلب نروید
اگر قرار است در مورد یک فناوری جدید در طی چند جلسه بحث کنید، لازم است یک جلسه کامل در مورد «چرا به این فناوری نیاز داریم» توضیح دهید. بنابراین ذکر اینکه بدون مقدمه به سراغ کدنویسی می‌رویم، سؤالات بسیاری را به جا خواهند گذاشت مانند ... «مشکل روش‌‌های قبلی چی هست؟» «مزیت این روش جدید در کجاست؟» و تا نتوانید این مسایل را شرح دهید، کار شما خریدار نخواهد داشت.

18) در زبان فارسی نیم فاصله‌ها را رعایت کنید
در نگارش زبان فارسی فرق است بین «آمده ام» و «آمده‌ام» و یا «می شود» را باید «می‌شود» نوشت. می‌شود اندکی وقت گذاشت و با مبحث نیم فاصله آشنا شد .

در کل کار کردن در یک محیط گروهی و چند نویسنده‌ای، به مرور زمان تجربه‌هایی را به همراه خواهند داشت که با به اشتراک گذاشتن آن‌ها و یا طرح صریح آن‌ها، می‌توان از تکرار اشتباهات متداول جلوگیری کرد.

 
مطالب
جایگزین کردن jQuery با JavaScript خالص - قسمت ششم - رخدادهای مرورگرها
واکنش نشان دادن به تغییرات صفحات وب، قسمت مهم و عمده‌ای از کار توسعه‌ی برنامه‌های وب را تشکیل می‌دهد. مفاهیم مرتبط با DOM events از زمان IE 4.0 و Netscape Navigator version 2 به مرورگرها اضافه شدند و به مرور تکامل یافتند. پیش از ظهور مرورگرهای مدرن (به IE 9.0 و مرورگرهای پیش از آن، مرورگرهای «باستانی» گفته می‌شود) به علت عدم هماهنگی آن‌ها با استانداردهای وب و تفاوت روش‌های رخدادگردانی، jQuery نقش مهمی را در زمینه‌ی یکدست سازی کدهای مدیریت رخدادها در بین مرورگرهای مختلف ارائه کرد. اما با پیش‌رفت‌های صورت گرفته و هماهنگی بیشتر مرورگرها با استانداردهای وب، دیگر نیازی به jQuery برای ارائه‌ی کدهای یکدست رخدادگردانی نیست و کار مستقیم با API وب مرورگرها برای این منظور کافی است.


انواع رخدادها: بومی و سفارشی

دو رده بندی عمومی رخدادها در مرورگرها وجود دارند: بومی و سفارشی.
بومی‌ها همان‌هایی هستند که در مستندات رسمی استانداردهای وب ذکر شده‌اند؛ مانند click که توسط ماوس و یا صفحه کلید فعال می‌شود و یا load که در زمان بارگذاری کامل صفحه، تصاویر و یا یک iframe رخ می‌دهد.
رخدادهای سفارشی مواردی هستند که توسط یک کتابخانه‌ی خاص و یا جهت یک برنامه‌ی خاص تهیه شده‌اند. مانند یک رخداد سفارشی که زمان شروع آپلود یک فایل را اعلام می‌کند.
رخدادهای سفارشی که بدون jQuery ایجاد و رخ‌می‌دهند، توسط jQuery نیز قابل بررسی و مدیریت هستند و نه برعکس. به عبارتی رخدادهای سفارشی ایجاد شده‌ی توسط jQuery غیراستاندارد بوده و صرفا مختص به API آن هستند.
در این بین، شیء استاندارد Event کار اتصال رخدادهای سفارشی و استاندارد را انجام می‌دهد. هر نوع رخداد DOM (سفارشی و یا بومی)، توسط یک شیء Event بیان می‌شود که آن نیز به همراه تعدادی خاصیت و متد، جهت مدیریت این رخداد است. برای مثال رخداد click دارای خاصیت type ایی به نام click است که در شیء Event متناظر با آن تعریف شده‌است.


انتشار رخدادها در صفحه

در روزهای آغازین وب، Netscape روش event capturing را برای انتشار رخدادها در صفحه ارائه داد و در مقابل آن IE روش event bubbling را معرفی کرد که متضاد یکدیگر بودند. در سال 2000 با ارائه استاندارد DOM Level 2 Events Specification، این وضعیت تغییر کرد و شامل هر دو مورد event capturing و event bubbling است و در حال حاضر تمام مرورگرهای مدرن این استاندارد را پیاده سازی کرده‌اند. بر اساس این استاندارد، زمانیکه رویدادی خلق می‌شود، فاز capturing آغاز می‌گردد که از شیء window شروع، سپس به شیء document منتشر می‌شود و این روند تا رسیدن به المانی که سبب بروز رخداد شده‌است ادامه پیدا می‌کند. پس از پایان فاز capturing، فاز جدید bubbling شروع می‌شود. در این فاز، رخداد از تمام والدین شیء هدف عبور می‌کند تا به شیء window برسد.
برای مثال اگر سند HTML ما چنین تعریفی را داشته باشد و بر روی المان «child of child of one» کلیک شده باشد:
   <!DOCTYPE html> 
   <html>
   <head>
     <title>event propagation demo</title>
   </head>
   <body>
     <section>
       <h1>nested divs</h1>
       <div>one
        <div>child of one
          <div>child of child of one</div>
        </div>
      </div>
    </section>
  </body>
  </html>
این رخداد در فاز capturing از این المان‌ها عبور می‌کند:
1.window
2.document
3.<html>
4.<body>
5.<section>
6.<div>one
7.<div>child of one
8.<div>child of child of one
و در فاز Bubbling از این المان‌ها:
9.<div>child of child of one
10.<div>child of one
11.<div>one
12.<section>
13.<body>
14.<html>
15.document
16.window
هرچند به دلایل تاریخی و همچنین عدم پشتیبانی jQuery از فاز capturing، بیشتر از فاز Bubbling به صورت پیش‌فرض در رخدادگردانی استفاده می‌شود. همچنین صدور رخداد از المانی که آن‌را ایجاد کرده‌است، بیشتر منطقی به نظر می‌رسد تا عکس آن.
البته باید درنظر داشت که jQuery از روش ارائه شده‌ی توسط مرورگر برای فاز Bubbling استفاده نمی‌کند و این مسیر را خودش مجددا محاسبه و رخدادگردان‌های این مسیر را به صورت دستی اجرا می‌کند. به همین جهت کارآیی آن نسبت به روش توکار و بومی مرورگرها کمتر است.


ایجاد رخدادهای DOM و صدور آن‌ها در jQuery

برای نمایش ایجاد و صدور رخدادهای DOM با و بدون jQuery، از قطعه کد HTML زیر استفاده می‌کنیم:
   <div>
     <button type="button">do something</button>
   </div>
 
  <form method="POST" action="/user">
     <label>Enter user name:
       <input name="user">
     </label>
     <button type="submit">submit</button>
  </form>
jQuery به همراه دو متد trigger و triggerHandler برای ایجاد و انتشار رخدادها در طول DOM است. متد trigger سبب ایجاد، صدور و انتشار یک رخداد به تمام والدهای شیء صادر کننده‌ی رخداد می‌شود. نوع این انتشار نیز bubbling است. متد triggerHandler، فقط بر روی المانی که فراخوانی می‌شود، عمل کرده و سبب انتشار این رخداد از طریق bubbling نمی‌شود:
   // submits the form 
   $('FORM').trigger('submit');
 
   // submits the form by clicking the button 
   $('BUTTON[type="submit"]').trigger('click');
 
   // focuses the text input 
   $('INPUT').trigger('focus');
 
  // removes focus from the text input 
  $('INPUT').trigger('blur');
در این مثال‌ها توسط متد trigger، به دو روش سبب submit یک فرم و همچنین در ابتدا سبب focus یک تکست باکس و سپس رفع آن شده‌ایم.
هرچند روش دومی نیز در jQuery API برای انجام همینکارها نیز پیش بینی شده‌است:
   // submits the form 
   $('FORM').submit();
 
   // submits the form by clicking the button 
   $('BUTTON[type="submit"]').click();
 
   // focuses the text input 
   $('INPUT').focus();
 
  // removes focus from the text input 
  $('INPUT').blur();
در اینجا به ازای هر رخداد، یک نام مستعار در jQuery API تدارک دیده شده‌است.

در ادامه فرض کنید یک دکمه داخل یک div قرار گرفته‌است و آن div نیز به همراه یک مدیریت کننده‌ی رخداد کلیک است. در این حالت اگر بخواهیم با کلیک بر روی دکمه سبب اجرای رویدادگردان div والد نشویم، می‌توان از متد triggerHandler استفاده کرد:
  // clicks the first button - the click event does not bubble 
  $('BUTTON[type="button"]').triggerHandler('click');


ایجاد رخدادهای DOM و صدور آن‌ها در جاوا اسکریپت (بدون استفاده از jQuery)

در web API مرورگرها، برای انجام بروز رخدادهای معادل مثالی که با jQuery مطرح شد، می‌توان متدهای بومی متناظر با این رخدادها را بر روی المان‌ها فراخوانی کرد:
   // submits the form 
   document.querySelector('FORM').submit();
 
   // submits the form by clicking the button 
   document.querySelector('BUTTON[type="submit"]').click();
 
   // focuses the text input 
   document.querySelector('INPUT').focus();
 
  // removes focus from the text input 
  document.querySelector('INPUT').blur();
قطعه کد فوق به علت استفاده‌ی از querySelector، با IE 8.0 و تمام مرورگرهای پس از آن سازگار است.
متدهای توکار و بومی click ،focus و blur بر روی تمام عناصر DOM که از اینترفیس HTMLElement مشتق شده باشند، وجود دارند. متد submit فقط بر روی المان‌هایی از نوع <form> وجود دارد و قابل فراخوانی است.
باید دقت داشت که فراخوانی متدهای click و submit از نوع bubbling است؛ اما متدهای focus و blur خیر. از این جهت که این دو رخداد فاز capturing را سبب می‌شوند.

متدهای یاد شده را توسط سازنده‌ی شیء Event و یا متد createEvent شیء document نیز می‌توان ایجاد کرد. یکی از کاربردهای آن، ارائه‌ی رفتاری سفارشی مانند triggerHandler جی‌کوئری است:
   var clickEvent;

   if (typeof Event === 'function') {
     clickEvent = new Event('click', {bubbles: false});
   }
   else {
       clickEvent = document.createEvent('Event');
      clickEvent.initEvent('click', false, true);
  }

  document.querySelector('BUTTON[type="button"]').dispatchEvent(clickEvent);
کار با سازنده‌ی شیء Event در تمام مرورگرهای جدید، منهای IE (تمام نگارش‌های آن) پشتیبانی می‌شود. در اینجا اگر این پشتیبانی وجود داشت، از خاصیت bubbles: false شیء Event استفاده می‌شود و اگر مرورگری قدیمی بود، از متد document.createEvent برای این منظور کمک گرفته می‌شود. در این حالت دومین پارامتر متد initEvent، همان bubbles است.


ایجاد و صدور رخدادهای سفارشی

فرض کنید در حال تهیه‌ی کتابخانه‌ای هستیم که افزودن و حذف آیتم‌ها را به یک گالری عکس ارائه می‌دهد. می‌خواهیم روشی را در اختیار مصرف کننده قرار دهیم تا بتواند به این رخدادهای سفارشی (غیر استانداردی که جزو W3C نیستند) گوش فرا دهد.
در جی‌کوئری برای ایجاد رخدادهای سفارشی به صورت زیر عمل می‌شود:
  // Triggers a custom "image-removed" element, 
  // which bubbles up to ancestor elements.
$libraryElement.trigger('image-removed', {id: 1});
در اینجا نیز صدور رخدادها همانند قبل و توسط همان متد trigger است. اما مشکلی که با آن وجود دارد این است که گوش فرا دهنده‌ی به این رخداد نیز باید توسط جی‌کوئری ارائه شود و خارج از این کتابخانه قابل دریافت و پیگیری نیست.
در خارج از جی‌کوئری و توسط web API استاندارد مرورگرها ایجاد و صدور رخدادهای سفارشی به همراه bubbling آن به صورت زیر است:
  var event = new CustomEvent('image-removed', {
    bubbles: true,
    detail: {id: 1}
  });
  libraryElement.dispatchEvent(event);
البته باید به‌خاطر داشته باشید این روش صرفا با مرورگرهای جدید (منهای تمام نگارش‌های IE) کار می‌کند. در اینجا اگر نیاز به ارائه‌ی راه حلی سازگار با IE نیز وجود داشت می‌توان از document.createEvent استفاده کرد:
  var event = document.createEvent('CustomEvent');
  event.initCustomEvent('image-removed', false, true, {id: 1});
  libraryElement.dispatchEvent(event);
و اگر بخواهیم بررسی وجود IE و یا پشتیبانی از CustomEvent را نیز قید کنیم، به قطعه کد زیر خواهیم رسید که با تمام مرورگرهای موجود کار می‌کند:
  var event;
 
   // If the `CustomEvent` constructor function is not supported, 
   // fall back to `createEvent` method. 
   if (typeof CustomEvent === 'function') {
     event = new CustomEvent('image-removed', {
       bubbles: true,
       detail: {id: 1}
     });
  }
  else {
      event = document.createEvent('CustomEvent');
      event.initCustomEvent('image-removed', false, true, {
        id: 1
      });
  }

  libraryElement.dispatchEvent(event);


گوش فرادادن به رخدادهای صادر شده، توسط jQuery

در جی‌کوئری با استفاده از متد on آن می‌توان به تمام رخدادهای استاندارد و همچنین سفارشی گوش فرا داد:
  $(window).on('resize', function() {
     // react to new window size 
  });
در ادامه برای حذف تمام گوش فرا دهنده‌های به رخداد resize می‌توان از متد off آن استفاده کرد:
  // remove all resize listeners - usually a bad idea 
  $(window).off('resize');
اما مشکلی را که این روش به همراه دارد، از کار انداختن تمام قسمت‌های دیگری است که هم اکنون به صدور این رخداد گوش فرا می‌دهند.
روش بهتر انجام اینکار، ذخیره‌ی ارجاعی به متدی است که قرار است این رویداد گردانی را انجام دهد:
  var resizeHandler = function() {
      // react to new window size 
  };

  $(window).on('resize', resizeHandler);

  // ...later 
  // remove only our resize handler 
  $(window).off('resize', resizeHandler);
و در آخر تنها این گوش فرا دهنده‌ی خاص را در صورت نیاز غیرفعال می‌کنیم و نه تمام گوش فرادهنده‌های سراسر برنامه را.

همچنین اگر یک گوش فراهنده‌ی به رخدادی تنها قرار است یکبار در طول عمر برنامه اجرا شود، می‌توان از متد one استفاده کرد:
$(someElement).one('click', function() {
    // handle click event 
});
پس از یکبار اجرای رخدادگردان کلیک در اینجا، از کلیک‌های بعدی صرفنظر خواهد شد.


گوش فرادادن به رخدادهای صادر شده، توسط جاوا اسکریپت خالص (یا همان web API مرورگرها)

ابتدایی‌ترین روش گوش فرادادن به رخدادها که از زمان آغاز معرفی آن‌ها در دسترس بوده‌است، روش تعریف inline آن‌ها است:
  <button onclick="handleButtonClick()">click me</button>
در اینجا متد رویدادگردان به یکی از ویژگی المان انتساب داده می‌شود. مشکل این روش، نیاز به سراسری تعریف کردن متد handleButtonClick است و دیگر نمی‌توان آن‌را در فضای نامی خاص و یا سفارشی قرار داد.
روش دیگر ثبت رویدادگردان click، انتساب متد آن به خاصیت رخداد متناظری در آن المان ویژه است:
  buttonEl.onclick = function() {
    // handle button click 
  };
مزیت این روش، عدم نیاز به استفاده‌ی از متدهای سراسری است.
البته باید دقت داشت که یکی از دو روش یاد شده را می‌توانید استفاده کنید. در اینجا آخرین رویدادگردان متصل شده‌ی به المان، همواره تمام نمونه‌های موجود دیگر را بازنویسی می‌کند.
اگر نیاز به معرفی رویدادگردان‌های متعددی برای یک المان در ماژول‌های مختلف برنامه وجود داشت، از زمان IE 9.0 به بعد، متد addEventListener برای این منظور تدارک دیده شده‌است و syntax آن بسیار شبیه به متد on جی‌کوئری است:
  buttonEl.addEventListener('click', function() {
    // handle button click 
  });
در این حالت دیگر مشکل نیاز به متدهای سراسری و یا عدم امکان تعریف بیش از یک رویدادگردان خاص برای المانی مشخص، دیگر وجود ندارد.
برای نمونه معادل قطعه کد جی‌کوئری که پیشتر با متد on نوشتیم، با جاوا اسکریپت خالص به صورت زیر است:
  window.addEventListener('resize', function() {
    // react to new window size 
  });
در اینجا برای حذف یک رویدادگردان می‌توان از متد removeEventListener استفاده کرد. تفاوت مهم آن با متد off جی‌کوئری این است که در اینجا حتما باید مشخص باشد کدام رویدادگردان را می‌خواهید حذف کنید:
  var resizeHandler = function() {
      // react to new window size 
  };

  window.addEventListener('resize', resizeHandler);

  // ...later 
  // remove only our resize handler 
  window.removeEventListener('resize', resizeHandler);
یعنی روش حذف رویدادگردان‌ها در اینجا شبیه به مثال دومی است که برای متد off جی‌کوئری ارائه کردیم. ابتدا باید ارجاعی را به متد رویدادگردان تهیه کنیم و سپس بر اساس این ارجاع، امکان حذف آن وجود خواهد داشت.
در اینجا حتی امکان تعریف متد one جی‌کوئری نیز پیش بینی شده‌است (البته جزو استانداردهای جدید وب از سال 2016 است):
  someElement.addEventListener('click', function(event) {
     // handle click event 
  }, { once: true });
اگر بخواهیم متد one سازگار با مرورگرهای قدیمی‌تر را نیز ارائه کنیم، چنین شکلی را پیدا می‌کند:
  var clickHandler = function() {
    // handle click event 
    // ...then unregister handler 
    someElement.removeEventListener('click', clickHandler);
  };
  someElement.addEventListener('click', clickHandler);
در اینجا پس از بروز رخداد، کار removeEventListener آن به صورت خودکار صورت می‌گیرد.


کنترل انتشار رخدادها

فرض کنید می‌خواهیم جلوی انتخاب المان‌های صفحه مانند تصاویر و متن را توسط ماوس بگیریم. روش انجام اینکار با jQuery به صورت زیر است:
$(window).on('mousedown', function(event) {
    event.preventDefault();
});
و یا توسط web API مرورگرها به این صورت:
  window.addEventListener('mousedown', function(event) {
    event.preventDefault();
  });
مطابق «W3C DOM Level 3 Events specification» عملکرد پیش‌فرض رخداد mousedown با انتخاب متون و یا کشیدن و رها کردن المان‌ها آغاز می‌شود. متد preventDefault یکی از متدهای شیء event است که به رویدادگردان‌های تعریف شده ارسال می‌شود و توسط آن عملکرد پیش‌فرض آن رخداد لغو می‌شود.

برای جلوگیری کردن از انتشار رخدادی مانند click جهت رسیدن به سایر رویدادگردان‌های ثبت شده‌ی در بین راه فاز bubbling، می‌توان از متد stopPropagation استفاده کرد. روش انجام اینکار در جی‌کوئری:
  $someElement.on('click', function(event) {
      event.stopPropagation();
  });
البته jQuery صرفا فاز انتشار از نوع bubbling را پشتیبانی می‌کند.
و با web Api جهت جلوگیری از انتشار رخدادها در فاز capturing (این تنها راه مدیریت فاز capturing است):
  // stop propagation during capturing phase 
  someElement.addEventListener('click', function(event) {
      event.stopPropagation();
  }, true);
و یا استفاده از web API برای جلوگیری از انتشار رخدادها در فاز bubbling:
  // stop propagation during bubbling phase 
  someElement.addEventListener('click', function(event) {
      event.stopPropagation();
  });
البته باید درنظر داشت که متد stopPropagation از انتشار رخدادها به سایر گوش فرا دهنده‌های همان المان صادر کننده‌ی رخداد جلوگیری نمی‌کند. برای این منظور باید از متد stopImmediatePropagation استفاده کرد؛ در جی‌کوئری:
  $someElement.on('click', function(event) {
      event.stopImmediatePropagation();
  });
و توسط web API مرورگرها:
  someElement.addEventListener('click', function(event) {
      event.stopImmediatePropagation();
  });

یک نکته: در این حالت اگر متد رویدادگردانی مقدار false را برگرداند، به معنای فراخوانی هر دوی متد preventDefault و stopPropagation است.


ارسال اطلاعات به رویدادگردان‌ها

روش ارسال اطلاعات اضافی به رویداد گردان‌ها در جی‌کوئری به صورت زیر است:
  $uploaderElement.trigger('uploadError', {
    filename: 'picture.jpeg'
  });
و رویدادگردان گوش فرا دهنده‌ی به آن، به این نحو می‌تواند به filename دسترسی پیدا کند:
  $uploaderParent.on('uploadError', function(event, data) {
     showAlert('Failed to upload ' + data.filename);
  });
در اینجا دومین پارامتر تعریف شده، امکان دسترسی به تمام خواص سفارشی ارسالی را میسر می‌کند.

روش انجام اینکار با web API مرورگرها به صورت زیر است:
   // send the failed filename w/ an error event
  var event = new CustomEvent('uploadError', {
     bubbles: true,
     detail: {filename: 'picture.jpeg'}
   });
   uploaderElement.dispatchEvent(event);
 
   // ...and this is a listener for the event 
   uploaderParent.addEventListener('uploadError', function(event) {
      showAlert('Failed to upload ' + event.detail.filename);
  });
این روش با تمام مرورگرهای مدرن (منهای تمام نگارش‌های IE) کار می‌کند و روش دسترسی به خاصیت detail سفارشی تعریف و ارسال شده، از طریق همان خاصیت event ارسالی به رویدادگردان است.
و اگر می‌خواهید از IE هم پشتیبانی کنید، روش جایگزین کردن شیء CustomEvent با createEvent به صورت زیر است:
  // send the failed filename w/ an error event 
   var event = document.createEvent('CustomEvent');
   event.initCustomEvent('uploadError', true, true, {
     filename: 'picture.jpeg'
   });
   uploaderElement.dispatchEvent(event);
 
   // ...and this is a listener for the event 
   uploaderParent.addEventListener('uploadError', function(event) {
    showAlert('Failed to upload ' + event.detail.filename);
  });


متوجه شدن زمان بارگذاری یک شیء در صفحه

در حین توسعه‌ی برنامه‌های وب، با این نوع سؤالات زیاد مواجه خواهید شد: چه زمانی تمام و یا بعضی از المان‌های صفحه کاملا بارگذاری و رندر شده‌اند؟
پاسخ به این نوع سؤالات در W3C UI Events specification توسط رویداد استاندارد load داده شده‌است.

- چه زمانی تمام المان‌های موجود در صفحه کاملا بارگذاری و رندر شده و همچنین شیوه‌نامه‌های تعریف شده نیز به آن‌ها اعمال گردیده‌اند؟
روش انجام اینکار با jQuery:
  $(window).on('load', function() {
    // page is fully rendered 
  });
و توسط web API بومی مرورگرها که بسیار مشابه نمونه‌ی jQuery است:
  window.addEventListener('load', function() {
    // page is fully rendered 
  });

- چه زمانی markup استاتیک صفحه‌ی جاری در جای خود قرار گرفته‌اند؟
اهمیت این موضوع، به دسترسی به زمان مناسب و امن ایجاد تغییرات در DOM بر می‌گردد. برای این منظور رویداد استاندارد DOMContentLoaded پیش‌بینی شده‌است که زودتر از رویداد load، در دسترس برنامه نویس قرار می‌گیرد. در جی‌کوئری توسط یکی از دو روش معروف زیر به رویداد یاد شده دسترسی خواهید داشت:
  $(document).ready(function() {
    // markup is on the page 
  });

  //or
  $(function() {
    // markup is on the page 
  });
و معادل web API آن در تمام مرورگرهای جدید، همان تعریف رویدادگردانی برای DOMContentLoaded استاندارد است:
  document.addEventListener('DOMContentLoaded', function() {
    // markup is on the page 
  });

یک نکته: بهتر است این تعریف web API را پیش از تگ‌های <link> قرار دهید. زیرا بارگذاری آن‌ها، اجرای هر نوع اسکریپتی را تا زمان پایان عملیات، سد می‌کند.

- چه زمانی المانی خاص در صفحه بارگذاری شده‌است و چه زمانی بارگذاری یک المان با شکست مواجه شده‌است؟
در جی‌کوئری توسط بررسی رویدادهای load و error می‌توان به وضعیت نهایی بارگذاری المان‌هایی خاص دسترسی یافت:
   $('IMG').on('load', function() {
     // image has successfully loaded 
   });
 
   $('IMG').on('error', function() {
     // image has failed to load 
   });
روش انجام اینکار با web API مرورگرها نیز یکی است:
  document.querySelector('IMG').addEventListener('load', function() {
    // image has successfully loaded 
  });

  document.querySelector('IMG').addEventListener('error', function() {
    // image has failed to load 
  });

- جلوگیری از ترک اتفاقی صفحه‌ی جاری
گاهی از اوقات نیاز است برای از جلوگیری از تخریب صفحه‌ی جاری و از دست رفتن اطلاعات ذخیره نشده‌ی کاربر، اگر بر روی دکمه‌ی close بالای صفحه کلیک کرد و یا کاربر به اشتباه به صفحه‌ی دیگری هدایت شد، جلوی اینکار را بگیریم. برای این منظور رخداد استاندارد beforeunload درنظر گرفته شده‌است. روش استفاده‌ی از این رویداد در جی‌کوئری:
  $(window).on('beforeunload', function() {
    return 'Are you sure you want to unload the page?';
  });
و در web API مرورگرها:
  window.addEventListener('beforeunload', function(event) {
    var message = 'Are you sure you want to unload the page?';
    event.returnValue = message;
    return message;
  });
در حالت web API بعضی از مرورگرها از نتیجه‌ی متد استفاده می‌کنند و بعضی دیگر از مقدار خاصیت event.returnValue. به همین جهت هر دو مورد در اینجا مقدار دهی شده‌اند.