نظرات مطالب
SFDown
سلام آقای نصیری
ممنون از مطلب مفیدتون.
میخواستم بدونم :
1)در هر بار استفاده از فایرفاکس آی پی من ابتدا جایی در فایر فاکس ذخیره میشه؟و فایر فاکس از طریق این آی پی محل من رو پیدا میکنه؟
2)فایر فاکس در هدر Requestای که مثلا برای SourceForge میفرسته این محل رو هم میفرسته؟
ببخشید من مکانیزم این کار رو خوب متوجه نشدم
سپاسگزار
مطالب
خواندنی‌های 12 تیر

اس کیوال سرور

امنیت

توسعه وب

دات نت فریم ورک

دبلیو سی اف

دبلیو پی اف و سیلور لایت

سایت‌های ایرانی

شیرپوینت

لینوکس

متفرقه

محیط‌های مجتمع توسعه

مرورگرها

مسایل انسانی، اجتماعی و مدیریتی برنامه نویسی

پی اچ پی

مطالب
انجام عملیات طولانی مدت با Web Workers
امروزه استفاده از صفحات وب، در همه امور به خوبی به چشم می‌خورد و تاثیر این فناوری را می‌توان در تمام عرصه‌های تولید و استفاده از نرم افزار دید. web worker یکی از فناوری‌های تحت وب بوده که توسط W3C ارائه شده است. وب ورکر به شما اجازه می‌دهد تا بتوانید عملیاتی را که نیاز به زمان زیادی برای پردازش دارد، در پشت صحنه انجام دهید؛ بدون اینکه وقفه‌ای در پردازش UI ایجاد شود. وب ورکر حتی به شما اجازه می‌دهد چند thread را همزمان اجرا کنید و پردازش‌هایی موازی یکدیگر داشته باشید. از آنجا که وب ورکرها یک ترد پردازشی جدا از UI به حساب می‌آیند، شما دسترسی به DOM ندارید؛ ولی می‌توانید از طریق ارسال پیام، با صفحه وب تعامل داشته باشد.

قبل از استفاده از وب ورکر، بهتر هست مرورگر را بررسی کنیم که آیا از این قابلیت پشتیبانی می‌کند یا خیر؟ روش بررسی کردن این قابلیت، شیوه‌های مختلفی دارد که به تعدادی از آن‌ها اشاره می‌کنیم:
typeof(Worker) !== "undefined"

 <script src="/js/modernizr-1.5.min.js"></script>
Modernizr.webworkers
هر کدام از عبارات بالا را اگر در شرطی بگذارید و جواب true بازگردانند به معنی پشتیبانی مرورگر این ویژگی است. modernizr فریمورکی جهت بررسی قابلیت‌های موجود در مرورگر است.
نحوه پشتیبانی وب ورکرها در مروگرهای مختلف به شرح زیر است:

برای ایجاد یک وب ورکر ابتدا لازم است تا کدهای پردازشی را داخل یک فایل js جداگانه بنویسیم. در این مثال ما قصد داریم که شمارنده‌ای را بنویسیم:
var i=0;

function timedCount() {
    i=i+1;
    postMessage(i);
    setTimeout("timedCount()", 500);
}

timedCount();

سپس در فایل HTML به شکل زیر وب ورکر را مورد استفاده قرار می‌دهیم. در سازنده Worker، ما آدرس فایل js را وارد می‌کنیم و برای توقف آن نیز از متد terminate استفاده می‌کنیم:
<!DOCTYPE html>
<html>
  <head>
    <script>
    var worker;

    function Start()
    {
      worker=new Worker("webwroker-even-numbers.js");
      worker.onmessage=(event)=>
      {
        document.getElementById("output").value=event.data;
      }
    }
    function Stop()
    {
      worker.terminate();
    }
    </script>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <input type="text" id="output"/>
    <button onclick="Start();">Start Worker</button>
<button onclick="Stop();">Stop Worker</button>
  </body>
</html>
در صورتی که خطایی در ورکر رخ بدهد، می‌توانید از طریق متد onerror آن را دریافت کنید:
      worker.onerror = function (event) {
            console.log(event.message, event);
         };
مقدار برگشتی event شامل اطلاعات زیادی در مورد خطاست که شامل نام و مسیر فایل خطا، شماره خط و شماره ستون خطا، پیام خطا و ... می‌شود.
همچنین برای ورکر هم می‌توانید پیامی را ارسال کنید، برای همین کد زیر را به کد ورکر اضافه می‌کنیم:
self.onmessage=(event)=>{
  i=event.data;
};
و در صفحه HTML هم کد دریافت پیام از ورکر را به شکل زیر تغییر میدهیم:
  worker.onmessage=(event)=>
      {
        document.getElementById("output").value=event.data;
        if(event.data==8)
        worker.postMessage(100);
      }
در این حالت اگر عدد به هشت برسد، ما به ورکر می‌گوییم که عدد را به صد تغییر بده.

روش‌های ارسال پیام
به این نوع ارسال پیام، Structure Cloning گویند و با استفاده از این شیوه، امکان ارسال نوع‌های مختلفی امکان پذیر شده است؛ مثل فایل‌ها، Blob‌ها، آرایه‌ها و کلاس‌ها و ... ولی باید دقت داشته باشید که این ارسال پیام‌ها به صورت کپی بوده و آدرسی ارجاع داده نمی‌شود و باید مدنظر داشته باشید که ارسال یک فایل، به فرض پنجاه مگابایتی، به خوبی قابل تشخیص است. طبق نظر گوگل، از حجم 32 مگابایت به بعد، این گفته به خوبی مشهود بوده و زمانبر می‌شود. به همین علت فناوری با نام Transferable Objects ایجاد شده است که "کپی صفر" Zero-Copy را پیاده سازی می‌کند و باعث بهبود عملگر کپی می‌شود:
worker.postMessage(arrayBuffer, [arrayBuffer]);
 پارامتر اول آن، آرایه بافر شده است و دومی هم لیست آیتم‌هایی است که قرار است انتقال یابند:
var ab = new ArrayBuffer(1);
worker.postMessage(ab, [ab]);
if (ab.byteLength) {
  alert('Transferables are not supported in your browser!');
} else {
  // Transferables are supported.
}
یا ارسال اطلاعات بیشتر:
worker.postMessage({data: ab1, moreData: ab2},
                   [ab1, ab2]);
در صورتیکه بتواند انتقال را انجام بدهد، byteLength حجم اطلاعات ارسالی را بر می‌گرداند؛ در غیر اینصورت عدد 0 را به عنوان خروجی بر می‌گرداند. در این پرسش و پاسخ می‌توانید نمونه یک انتقال و دریافت را در این روش، ببینید.
نمودار زیر مقایسه‌ای بین Structure Cloning و Transferable Objects است که توسط گوگل منتشر شده است:

RTT=Round Trip Time 

نمودار بالا برای یک فایل 32 مگابایتی است که زمان رفت به ورکر و پاسخ (برگشت از ورکر) را اندازه گرفته‌اند. در ستون‌های اول، این موضوع برای فایرفاکس به روش Structure Cloning  به مدت 302 میلی ثانیه زمان برد که همین موضوع برای Transferables حدود 6.6 میلی ثانیه زمان برد.

آقای اریک بایدلمن در بخش مهندسی کروم گوگل می‌گوید: همین سرعت به ما در انتقال تکسچرها و مش‌ها در WebGL کمک می‌کند.


استفاده از اسکریپت خارجی

در صورتیکه قصد دارید از یک اسکرپیت خارجی، در ورکر استفاده کنید، تابع importScripts برای اینکار ایجاد شده است:

importScripts('script1.js');
importScripts('script2.js');
که البته به طور خلاصه‌تری نیز می‌توان نوشت:
importScripts('script1.js', 'script2.js');

Inline Worker
اگر بخواهید در همان صفحه اصلی یک ورکر را ایجاد کنید و فایل جاوا اسکریپتی خارجی نداشته باشید، می‌توانید از inline worker استفاده کنید. در این روش باید یک نوع blob را ایجاد کنید:
var blob = new Blob([
    "onmessage = function(e) { postMessage('msg from worker'); }"]);

// یک آدرس همانند آدرس ارجاع به فایل درست میکند
var blobURL = window.URL.createObjectURL(blob);

var worker = new Worker(blobURL);
worker.onmessage = function(e) {
  // e.data...
};
worker.postMessage(); // ورکر آغاز می‌شود
کاری که متد حیرت انگیز createObjectURL انجام می‌دهد این است که از داده‌های ذخیره شده در یک blob یا نوع فایل، یک آدرس ارجاعی شبیه آدرس زیر را ایجاد می‌کند:
blob:http://localhost/c745ef73-ece9-46da-8f66-ebes574789b1
آدرس‌هایی که این متد تولید می‌کند، یکتا بوده و تا پایان عمر صفحه، اعتبار دارند. به همین دلیل هر موقع به آن‌ها نیاز نداشتید، از دست آن‌ها خلاص شوید، تا حافظه به هدر نرود. برای آزادسازی حافظه می‌توان دستور زیر را به کار برد:
window.URL.revokeObjectURL(blobURL);
مرورگر کروم با دستور زیر به شما اجازه می‌دهد همه آدرس‌های blob‌ها را ببینید:
chrome://blob-internals
اشتراک‌ها
Chrome و قابلیت DirectWrite

هدف قابلیت DirectWrite در کروم، بهبود نمایش و خوانایی فونت‌ها است؛ اما ... اگر فونت‌های فارسی را به هم ریخته مشاهده می‌کنید، نیاز است این قابلیت را خاموش کنید: chrome://flags -> Disable DirectWrite 

Chrome و قابلیت DirectWrite
مطالب
ایجاد کپچایی (captcha) سریع و ساده در ASP.NET MVC 5

در این مثال به کمک MVC5، یک کپچای ساده و قابل فهم را تولید و استفاده خواهیم کرد. این نوشته بر اساس این مقاله  ایجاد شده و جزئیات زیادی برای درک افراد مبتدی به آن افزوده شده است که امیدوارم راهنمای مفیدی برای علاقمندان باشد.

با کلیک راست بر روی پوشه کنترلر، یک کنترلر به منظور ایجاد کپچا بسازید و اکشن متد زیر را در آن کنترلر ایجاد کنید: 

public class CaptchaController : Controller
    {
        public ActionResult CaptchaImage(string prefix, bool noisy = true)
        {
            var rand = new Random((int)DateTime.Now.Ticks);
            //generate new question
            int a = rand.Next(10, 99);
            int b = rand.Next(0, 9);
            var captcha = string.Format("{0} + {1} = ?", a, b);

            //store answer
            Session["Captcha" + prefix] = a + b;

            //image stream
            FileContentResult img = null;

            using (var mem = new MemoryStream())
            using (var bmp = new Bitmap(130, 30))
            using (var gfx = Graphics.FromImage((Image)bmp))
            {
                gfx.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
                gfx.SmoothingMode = SmoothingMode.AntiAlias;
                gfx.FillRectangle(Brushes.White, new Rectangle(0, 0, bmp.Width, bmp.Height));

                //add noise
                if (noisy)
                {
                    int i, r, x, y;
                    var pen = new Pen(Color.Yellow);
                    for (i = 1; i < 10; i++)
                    {
                        pen.Color = Color.FromArgb(
                        (rand.Next(0, 255)),
                        (rand.Next(0, 255)),
                        (rand.Next(0, 255)));

                        r = rand.Next(0, (130 / 3));
                        x = rand.Next(0, 130);
                        y = rand.Next(0, 30);

                        gfx.DrawEllipse(pen, x - r, y - r, r, r);
                    }
                }

                //add question
                gfx.DrawString(captcha, new Font("Tahoma", 15), Brushes.Gray, 2, 3);

                //render as Jpeg
                bmp.Save(mem, System.Drawing.Imaging.ImageFormat.Jpeg);
                img = this.File(mem.GetBuffer(), "image/Jpeg");
            }

            return img;
        }

همانطور که از کد فوق پیداست، دو مقدار a و b، به شکل اتفاقی ایجاد می‌شوند و حاصل جمع آنها در یک Session نگهداری خواهد شد. سپس تصویری بر اساس تصویر a+b ایجاد می‌شود (مثل 3+4). این تصویر خروجی این اکشن متد است. به سادگی می‌توانید این اکشن را بر اساس خواسته خود اصلاح کنید؛ مثلا به جای حاصل جمع دو عدد، از کاربرد چند حرف یا عدد که بصورت اتفاقی تولید کرده‌اید، استفاده نمائید.

فرض کنید می‌خواهیم کپچا را هنگام ثبت نام استفاده کنیم.

در فایل AccountViewModels.cs در پوشه مدل‌ها در کلاس RegisterViewModel  خاصیت زیر را اضافه کنید:

[Required(ErrorMessage = "لطفا {0} را وارد کنید")]
         [Display(Name = "حاصل جمع")]
         public string Captcha { get; set; }

حالا در پوشه View/Account به فایل Register.Cshtml خاصیت فوق را اضافه کنید:

<div class="form-group">
                        <input type="button" value="" id="refresh" />

                        @Html.LabelFor(model => model.Captcha)

                        <img alt="Captcha" id="imgcpatcha" src="@Url.Action("CaptchaImage","Captcha")" style="" />
                    </div>

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

حالا کد ایجکسی برای آپدیت کپچا توسط دکمه refresh را  به شکل زیر بنویسید (من در پایین ویوی Register، اسکریپت زیر را قرار دادم): 

<script type="text/javascript">
    $(function () {
        $('#refresh').click(function () {


            $.ajax({
                url: '@Url.Action("CaptchaImage","Captcha")',
                type: "GET",
                data: null
            })
            .done(function (functionResult) {
                $("#imgcpatcha").attr("src", "/Captcha/CaptchaImage?" + functionResult);
            });

        });
    });
</script>

آنچه در url نوشته شده است، شاید اصولی‌ترین شکل فراخوانی یک اکشن متد باشد. این اکشن در ابتدای مقاله تحت کنترلری به نام Captcha معرفی شده بود و خروجی آن آدرس یک فایل تصویری است. نوع ارتباط، Get است و هیچ اطلاعاتی به اکشن متد فرستاده نمیشود، اما اکشن متد ما آدرسی را به ما برمی‌گرداند که تحت نام FunctionResult آن را دریافت کرده و به کمک کد جی کوئری، مقدارش را در ویژگی src تصویر موجود در صفحه جاری جایگزین می‌کنیم. دقت کنید که برای دسترسی به تصویر، لازم است جایگزینی آدرس، در ویژگی src به شکل فوق صورت پذیرد.*

تنها کار باقیمانده اضافه کردن کد زیر به ابتدای اکشن متد Register درون کنترلر Account است. 

if (Session["Captcha"] == null || Session["Captcha"].ToString() != model.Captcha)
            {
                ModelState.AddModelError("Captcha", "مجموع اشتباه است");
            }

واضح است که اینکار پیش از شرط if(ModelState.IsValidate) صورت میگیرد و وظیفه شرط فوق، بررسی ِ برابریِ مقدار Session تولید شده در اکشن CaptchaImage  (ابتدای این مقاله) با مقدار ورودی کاربر است. (مقداری که از طریق خاصیت تولیدی خودمان  به آن دسترسی داریم) . بدیهی‌است اگر این دو مقدار نابرابر باشند، یک خطا به ModelState اضافه می‌شود و شرط ModelState.IsValid که در اولین خط بعد از کد فوق وجود دارد، برقرار نخواهد بود و پیغام خطا در صفحه ثبت نام نمایش داده خواهد شد.

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


* اصلاح : دقت کنید بدون استفاده از ایجکس هم میتوانید تصویر فوق را آپدیت کنید:

  $('#refresh').click(function () {
         
            var d = new Date();
            $("#imgcpatcha").attr("src", "Captcha/CaptchaImage?" + d.getTime());

        });

رویداد کلیک را با کد فوق جایگزین کنید؛ دو نکته در اینجا وجود دارد :

یک. استفاده از زمان در انتهای آدرس به خاطر مشکلاتیست که فایرفاکس یا IE با اینگونه آپدیت‌های تصویری دارند. این دو مرورگر (بر خلاف کروم) تصاویر را نگهداری میکنند و آپدیت به روش فوق به مشکل برخورد میکند مگر آنکه آدرس را به کمک اضافه کردن زمان آپدیت کنید تا مرورگر متوجه داستان شود

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

مطالب
AngularJS #1
پیش از اینکه آموزش AngularJs را شروع کنیم بهتر است با مفهوم برنامه‌های تک صفحه ای وب  و یا  Single Page Web Applications آشنا شویم؛ چرا که AngularJS برای توسعه هر چه ساده‌تر و قوی‌تر این گونه برنامه‌ها متولد شده است.

Single Page Application
برای درک چگونگی کارکرد این برنامه ها، مثالی را میزنیم که هر روزه با آن سرو کار دارید، یکی از نمونه‌های کامل و قدرتمند برنامه‌های Single Page Application و یا به اختصار SPA، سرویس پست الکترونیکی Google و یا همان Gmail است.
اجازه بدهید تا ویژگی‌های SPA را با بررسی Gmail انجام دهم، تا به درک روشنی از آن برسید:

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

تغییر URL در نوار آدرس مرورگر
وقتی شما بر روی یک ایمیل کلیک می‌کنید و آن ایمیل را بدون Reload شدن مجدد صفحه مشاهده می‌کنید، آدرس صفحه در مرورگر نیز تغییر می‌کند. خب مزیت این ویژگی چیست؟ مزیت این ویژگی در این است که هر ایمیل شما دارای یک آدرس منحصر به فرد است و به شما امکان Bookmark کردن آن لینک، باز کردن آن در یک Tab جدید و یا حتی ارسال آن به دوستان خود را دارید. حتی اگر این مطلب را جدا از Gmail در نظر بگیریم، به موتور‌های جست و جو کمک می‌کند، تا هر صفحه را جداگانه Index کنند؛ جدا از اینکه وبسایت ما SPA است. همچنین این کار یک مزیت مهم دیگر نیز دارد؛ و آن کار کردن کلیدهای back و forward مرورگر، برای بازگشت به صفحات پیمایش شده قبلی است.
شاید قبل از بیان این ویژگی با خود گفته باشید که پیاده سازی Reload نشدن صفحات با AJAX آن چنان کار پیچیده ای نیست. بله درست است، اما آیا شما قبل از این راه حلی برای تغییر URL اندیشیده بودید؟ مطمئنا شما هم صفحات وب زیادی را دیده اید که همه‌ی صفحات آن دارای یک URL در نوار آدرس مرورگر هستند و هیچگاه تغییر نمی‌کنند و با باز کردن یک لینک در یک Tab جدید، باز همان صفحه‌ی تکراری را مشاهده می‌کنند! و یا بدتر از همه که دکمه‌ی back مرورگر غیر عادی عمل می‌کند. بله، اینها تنها تعدادی از صدها مشکلات رایج سیستم‌های نوشته شده ای است که سعی کردند همه‌ی کارها در یک صفحه انجام شود.

Cache شدن اطلاعات دریافتی
شاید خیلی‌ها ویژگی‌های فوق را برای یک SPA کافی بدانند، اما تعدادی هم مانند نگارنده وجود یک کمبود را حس می‌کنند و آن کش شدن اطلاعات دریافتی در مرورگر است. Gmail این امکان را به خوبی پیاده سازی کرده است. لیست ایمیل‌های دریافتی در بار اول از سرور دریافت می‌شود، سپس شما بر روی یک ایمیل کلیک و آن را مشاهده می‌کنید. حال به لیست ایمیل‌های دریافتی بازگردید، آیا رفت و برگشتی به سرور انجام می‌شود؟ مسلما خیر. حتی اگر دوباره بر روی آن ایمیل مشاهده شده ، کلیک کنید، بدون رفت و برگشتی به سرور آن ایمیل را مشاهده می‌کنید.
کش شدن اطلاعات سبب می‌شود که بار سرور خیلی کاهش یابد و رفت و آمدهای بیهوده صورت نگیرد. کش شدن داده‌ها یک مزیت دیگر نیز دارد و آن تبدیل برنامه‌های معمول وب stateless به برنامه‌های شبه دسکتاپ state full است. 
  
تکنیک AJAX در پیاده سازی امکانات فوق نقشی اساسی را بازی می‌کند. کمی به عقب برمیگردیم یعنی زمانی که AJAX برای اولین بار مطرح شد و هدف اصلی به وجود آمدن آن پیاده سازی برنامه‌های وب به شکل دسکتاپ بود و این کار از طریق انجام تمامی ارتباطات سرور با  XMLHttpRequest  امکان پذیر می‌شد. شاید آن زمان با توجه به محدودیت تکنولوژی‌ها موجود این کار به صورت تمام و کمال امکان پذیر نبود، اما امروزه به بهترین شکل ممکن قابل پیاده سازی است.

 شاید اکنون این سوال پیش بیاید که چرا باید وبسایت خود را به شکل SPA طراحی کنیم؟
برای پاسخ دادن به این سوال باید گفت که سیستم‌های وب  امروزی به دو دسته‌ی زیر تقسیم می‌شوند:
Web Documents و یا همان وب سایت‌های معمول
Web Applications و یا همان Single Page Web Applications
اگر هدف شما طراحی یک وب سایت معمول است که هدف آن، نمایش یک سری اطلاعات است و به قولی دارای محتواست، مطمئنا پیاده سازی این سیستم به صورت SPA کاری بیهوده به نظر می‌آید؛ ولی اگر هدفتان نوشتن سیستم هایی مثل Gmail، Google Maps، Azure، Facebook و ... است، پیاده سازی آن‌ها به صورت وب سایت‌های معمولی، غیر معقول به نظر می‌آید. حتی بخش‌های مدیریتی یک وبسایت هم می‌تواند به خوبی توسط SPA پیاده سازی شود، چرا که واقعا برای مدیریت اطلاعات یک وب سایت احتیاجی نیست، که  از این صفحه به آن صفحه جا به جا شد.

معرفی کتابخانه‌ی AngularJS
AngularJS فریم ورکی متن باز و نوشته شده به زبان جاوا اسکریپت است. هدف از به وجود آمدن این فریم ورک، توسعه هر چه ساده‌تر SPA‌ها با الگوی طراحی MVC و تست پذیری هر چه آسان‌تر آن‌ها است. این فریم ورک توسط یکی از محققان Google در سال 2009 به وجود آمد. بعد‌ها این فریم ورک تحت مجوز MIT به صورت متن باز در آمد و اکنون گوگل آن را حمایت می‌کند و توسط هزاران توسعه دهنده در سرتاسر دنیا، توسعه داده می‌شود.
قبل از اینکه به بررسی ویژگی‌های Angular بپردازم، بهتر است ابتدا مطلبی درباره‌ی به کارگیری Angular از Brad Green که کارمند گوگل است، بیان کنم.
در سال 2009 تیمی در گوگل مشغول انجام پروژه ای به نام Google Feedback بودند. آن‌ها سعی داشتند تا در طی چند ماه، به سرعت کد‌های خوب و تست پذیر بنویسند. پس از 6 ماه کدنویسی، نتیجه‌ی کار 17000 خط کد شد. در آن موقع یکی از اعضای تیم به نام Misko Hevery، ادعا کرد که می‌تواند کل این پروژه را در دو هفته به کمک کتابخانه‌ی متن بازی که در اوقات فراغت توسعه داده است، بازنویسی کند. Misko نتوانست در دو هفته این کار را انجام دهد. اما پس از سه هفته همه‌ی اعضای تیم را شگفت زده کرد. نتیجه‌ی کار تنها 1500 خط بود! همین باعث شد که ما بفهمیم که، Misko بر روی چیزی کاری میکند که ارزش دنبال کردن دارد.
پس از آن قضیه Misko و Brad بر روی Angular کار کردند و اکنون هم Angular توسط تیمی در گوگل و هزاران توسعه دهنده‌ی متن باز حرفه ای در سرتاسر جهان، درحال توسعه است.
فکر کنم همین داستان ذکر شده، قدرت فوق العاده زیاد این فریم ورک را برای همگان آشکار سازد.

ویژگی‌های AngularJS:

قالب‌های سمت کاربر (Client Side Templates): انگولار دارای یک template engine قدرتمند برای تعریف قالب است.

پیروی از الگوی طراحی MVC: انگولار، الگوی طراحی MVC را برای توسعه پیشنهاد می‌دهد و امکانات زیادی برای توسعه هر چه راحت‌تر با این الگو فراهم کرده است.

Data Binding: امکان تعریف انقیاد داده دوطرفه (Two-Way Data Binding) در این فریم ورک به راحتی هرچه تمام، امکان پذیر است.

Dependency Injection: این فریم ورک برای دریافت وابستگی‌های تعریف شده، دارای یک سیستم تزریق وابستگی توکار است.

تعریف Service‌های سفارشی: در این فریم ورک امکان تعریف سرویس‌های دلخواه به صورت ماژول وجود دارد. این ماژول‌های مجزا را به کمک سیستم تزریق وابستگی توکار Angular، به راحتی در هر جای برنامه می‌توان تزریق کرد.

تعریف Directive‌های سفارشی: یکی از جذاب‌ترین و قدرتمند‌ترین امکانات این فریم ورک، تعریف Directive‌های سفارشی است.  Directive ها، امکان توسعه HTML را فراهم کرده اند. توسعه‌ی HTML اکنون در قالب Web Components‌ها فراهم شده است، اما هنوز هم خیلی از مرورگر‌های جدید نیز از آن پشتیبانی نمی‌کنند.

فرمت کردن اطلاعات با استفاده از فیلترهای سفارشی: با استفاده از فیلترها میتوانید چگونگی الحاق شدن اطلاعات را برای نمایش به کاربر تایین کنید ؛ انگولار همراه با فیلترهای گوناگون مختلفی عرضه میشود که میتوان برایه مثال به فیلتر currency ، date ،uppercase کردن رشته‌ها و .... اشاره کرد همچنین شما محدود به فیلترهای تعریف شده در انگولار نیستید و آزادید که فیلترهای سفارشی خودتان را نیز تعریف کنید.

سیستم Routing: دارا بودن سیستم Routing  قدرتمند، توسعه SPA‌ها را بسیار ساده کرده است.

سیستم اعتبار سنجی: Angular دارای سیستم اعتبار سنجی توکار قدرتمند برای بررسی داده‌های ورودی است.

سرویس تو کار برای ارتباط با سرور: Angular دارای سرویس پیش فرض ارتباط با سرور به صورت AJAX است.

تست پذیری: Angular دارای بستری آماده برای تست کردن برنامه‌های نوشته شده است و از Unit Tests و Integrated End-to-End Test هم پشتیبانی می‌کند.

جامعه‌ی متن باز بسیار قوی
   
این‌ها فقط یک مرور کلی بر توانایی‌های این فریم ورک بود و در ادامه هر کدام از این ویژگی را به صورت دقیق بررسی خواهیم کرد.
در مقاله‌ی بعدی، به چگونگی نصب AngularJS خواهیم پرداخت. سپس، اولین کد خود را با استفاده از آن خواهیم نوشت و مطالب Client Side Templates و MVC را دقیق‌تر بررسی خواهیم کرد.
 
مطالب
شرح یک مشکل امنیتی با فایرفاکس
حدود دو ماه قبل دوبار از طریق میل‌باکس یاهو من به تمام contactهای تعریف شده در آن ایمیلی با محتوای زیر ارسال شده بود:

Hello,
you should definitely check this thing out http://www.newsl5.net/biz/?page=xyz

این ایمیل‌ها هم جعلی نبودند. یعنی واقعا از اکانت یاهوی من ارسال شده بودند و در قسمت sent وجود خارجی داشتند! فقط IP ارسال کننده آن (115.78.224.246) متعلق به کشور ویتنام بود (IP ارسال کننده را در هدر ایمیل ارسالی می‌توان مشاهده کرد).
این مساله باعث شد که من سیستم را چندین بار چک کنم؛ از لحاظ بحث ویروس تا اسپای‌ور و غیره. «هیچ» مشکلی مشاهده نشد.
مرحله بعد کمی در مورد یاهو جستجو کردم و مشخص شد که یاهو با session hijacking به شدت مشکل دارد. همچنین ابزار دیگری که می‌تواند به این session hijacking کمک کند خود «فایرفاکس» است. فایرفاکس حاوی گزینه‌ای است که سشن‌های قبلی شما را ذخیره می‌کند. زمانیکه مرورگر را بسته و پس از مدتی آن‌را باز می‌کنیم، یک راست و قشنگ همان سشن قبلی مثلا یاهو را بازیابی کرده و کار ادامه پیدا می‌کند.
کمی گشتم و این قابلیت رو به کل غیرفعال کردم. برای غیرفعال کردن آن «Disable Session Restore in Firefox» را در گوگل جستجو کنید.
و خلاصه آن به صورت زیر است:
در نوار آدرس فایرفاکس تایپ کنید about:config
در ادامه موارد زیر را یافته و غیرفعال کنید:
browser.sessionstore.resume_from_crash;false
browser.sessionstore.resume_session_once;false
browser.sessionstore.restore_pinned_tabs_on_demand;false
browser.sessionstore.restore_hidden_tabs;false
services.sync.prefs.sync.browser.sessionstore.restore_on_demand;false

راه ساده‌تر:
افزونه session manager را نصب کنید
در قسمت session manager options در برگه startup & shutdown آن کلا بحث ذخیره سازی سشن در حین بسته شدن مرورگر را غیرفعال کنید.


و به صورت خلاصه: تنظیمات پیش فرض فایرفاکس از لحاظ امنیتی مناسب نیستند.
ضمن اینکه ایمیل فوق رو من هفته‌ای یکی دو بار از تمام افرادی که می‌شناسم دریافت می‌کنم! به عبارتی خیلی‌ها گرفتار این مساله شده‌اند.
ذخیره سازی سشن‌ها به نظر کارها رو ساده می‌کنه. مرورگر رو باز می‌کنی همه چیز مثل قبل از بسته شدن آن است و ... همین یعنی مشکل امنیتی. خصوصا مراجعه به سایت‌ها و لینک‌هایی که از باگ‌های XSS سوء استفاده می‌کنند.
 
مطالب دوره‌ها
اعمال غیر همزمان و چند ریسمانی
تصور عموم بر آن است که اعمال غیر همزمان با چند ریسمانی به یک معنا هستند. این مورد الزاما صحیح نیست. برای مثال دریافت غیرهمزمان یک فایل را از اینترنت درنظر بگیرید. شاید اینطور به نظر برسد که در اینجا یک ترد جدید ایجاد شده و در آن کل کار دریافت فایل آغاز می‌گردد؛ اما خیر. ایجاد یک ترد جدید تنها در قسمت‌های خاصی از یک پروسه انجام می‌شود. همچنین از لحاظ فنی امکان انجام کل کار در یک ترد، بدون بلاک کردن آن وجود دارد. از این جهت که بیشتر زمان، جهت صبر کردن دریافت پاسخی از سرور صرف می‌شود. زمانیکه کلاینت درخواستی را ارسال می‌کند، دیگر کار خاصی را نمی‌تواند انجام دهد تا اینکه پاسخی را دریافت کند.
زمانیکه از یک API غیرهمزمان برای مدیریت چنین عملیاتی استفاده می‌شود، ترد جاری را در این حالت در خواب فرو می‌برد. برای اینکه کار بیشتری برای انجام وجود ندارد. همچنین با اینکه کلاینت درخواستی را ارسال می‌کند یا پاسخی را دریافت، برای مدیریت کل عملیات در اکثر اوقات نیازی به تردها ندارد. این سخت افزار شبکه‌ی نصب شده در سیستم است که عمده‌ی کار را انجام می‌دهد و نه برنامه. زمانیکه برنامه درخواست ارسال اطلاعاتی را بر روی شبکه ارائه می‌دهد، درایور سخت افزار شبکه است که به سخت افزار مرتبط فرمان می‌دهد چه اطلاعاتی را باید ارسال کند. اکثر اینگونه سخت افزارها قادرند اطلاعات را خارج از حافظه‌ی اصلی سیستم دریافت کنند. در اینجا درایور تنها باید به سخت افزار عنوان کند، چه اطلاعاتی را و به کجا باید ارسال کند. بنابراین CPU تنها در طی ارسال این فرمان است که مشغول می‌باشد و نه خارج از آن و این زمان اصلا در مقایسه با زمان ارسال اطلاعات توسط سخت افزار مرتبط، طولانی نیست. CPU  مجددا زمانی درگیر خواهد شد که سخت افزار شبکه، اطلاعاتی را دریافت کرده است و باز هم این زمان در مقایسه با زمان دریافت اطلاعات توسط سخت افزار شبکه بسیار کوتاه است.
اغلب کارهای IO به همین شکل هستند. شبیه به همین روند در حالت دسترسی به سخت دیسک وجود دارد. مدت زمانیکه CPU به دیسک کنترلر اعلام می‌کند چه اطلاعاتی را نیاز دارد در مقایسه با مدت زمانیکه دیسک کنترلر این اطلاعات را واقعا بارگذاری می‌کند، بسیار ناچیز است.
نمونه‌ی دیگر آن کار با بانک‌های اطلاعاتی است. در اغلب اوقات برنامه‌ی ما صرفا یک درخواست را به بانک اطلاعاتی ارائه می‌دهد و اصل عملیات در جایی دیگر و توسط موتور بانک اطلاعاتی، خارج از برنامه پردازش می‌گردد.
بنابراین جهت پردازش یک پروسه‌ی خاص، در بسیاری از مراحل آن تنها یک ترد کافی است و هدف اصلی اعمال غیرهمزمان، کاهش تعداد تردهایی است که برنامه جهت پردازش عملیاتی خاص، نیاز دارد. این نوع الگوریتم‌ها طوری طراحی شده‌اند تا تردها تنها زمانی بکار گرفته شود که واقعا CPU قرار است کار خاصی را انجام دهد و نه برای مثال زمانیکه دیسک کنترلر یا سخت افزار شبکه مشغول به کار هستند (و ویندوز به صورت توکار دارای یک چنین API ایی هست). این مساله در سمت کلاینت، سبب خواهد شد تا ترد UI آزاد شود و بتواند به درخواست‌های رسیده کاربر بهتر پاسخ دهد. همچنین این مساله در سمت سرور نیز بسیار مفید است، زیرا برنامه قادر خواهد شد تا به تعداد بیشتری از درخواست‌ها به صورت همزمان پاسخ دهد. زیرا با کاهش تعداد تردهای درگیر، مقیاس پذیری سیستم افزایش می‌یابد.
نظرات مطالب
بررسی بهبودهای پروسه‌ی Build در دات‌نت 8

یک نکته‌ی تکمیلی: تعدیل خطاهای بررسی امنیتی بسته‌های نیوگت در حالت کار offline در دات‌نت 8

اگر در پروژه‌ی خود، تنظیم گزارش اخطارها را به صورت خطا، فعال کرده باشید:

<PropertyGroup>
   <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

و ... از دات‌نت 8 هم استفاده می‌کنید، هربار با صدور فرمان dotnet build و یا dotnet restore، با خطای زیر مواجه خواهید شد:

warning NU1900: Error occurred while getting package vulnerability data: (more information)

البته یکبار که اطلاعات امنیتی بسته‌ها ذخیره شدند، ممکن است در طول یک روز دیگر شاهد این خطا نباشید، اما ... دوباره فردا تکرار خواهد شد و اگر بخواهید offline کار کنید، این خطا واقعا مشکل ساز می‌شود!

برای کنترل آن یا می‌توان به صورت زیر عمل کرد:

<PropertyGroup>
    <NuGetAudit>false</NuGetAudit>
</PropertyGroup>

که بررسی امنیتی بسته‌های نیوگت را کاملا غیرفعال می‌کند و یا می‌توان به صورت زیر، این بررسی را فقط به حالت Release خلاصه کرد:

<PropertyGroup>
  <NuGetAudit>true</NuGetAudit>
  <NuGetAuditMode>all</NuGetAuditMode>
  <NuGetAuditLevel>low</NuGetAuditLevel>
  <WarningsNotAsErrors Condition="'$(Configuration)' != 'Release'">
    $(WarningsNotAsErrors);NU1900;NU1901;NU1902;NU1903;NU1904
  </WarningsNotAsErrors>
</PropertyGroup>

در این حالت هرچند اخطارهای NU1900 و دردسترس نبودن اینترنت ظاهر می‌شوند، اما دیگر به‌عنوان خطا پردازش نخواهند شد (چون در قسمت WarningsNotAsErrors ذکر شده‌اند) و پروسه‌ی build را متوقف نمی‌کنند.

نظرات مطالب
امن سازی برنامه‌های ASP.NET Core توسط IdentityServer 4x - قسمت دهم- ذخیره سازی اطلاعات کاربران IDP در بانک اطلاعاتی
- خیر.
- میزان تغییرات آن‌ها بسته به اینکه پروژه‌ی API یا MVC باشند، در قسمت‌های 5 تا هفتم این سری به تفصیل و جداگانه بحث شده‌اند و به همراه یک پروژه‌ی انجام شده که از قسمت دوم شروع می‌شود و تا قسمت آخر تکمیل شده، هست. این سری فقط دو کلاینت API و MVC را بررسی کرده. سایر کلاینت‌ها را باید به مثال‌های خودشان مراجعه کنید.
- بله و خیر. قرار نیست کلاینت‌ها دیگر اطلاعات کاربران را ثبت کنند و قرار نیست دیگر در بانک اطلاعاتی خودشان به دنبال آن‌ها بگردند. سطوح دسترسی و claims در اینجا (قسمت‌های 6 و 8 این سری)، کار مدیریت سطوح دسترسی و دریافت آن‌ها را از IDP انجام می‌دهند. یعنی IDP فقط کار لاگین را برای آن‌ها انجام نمی‌دهد (authentication)، بلکه قسمتی از پروسه‌ی Authorization (تامین اطلاعات مورد نیاز جهت برقرار سطوح دسترسی، یا همان قسمت 6) را به نام Claims هم انجام می‌دهد. برنامه‌ی کلاینت با این claims تمام دسترسی‌ها لازم را برای تامین سیاست‌های داخلی خودش، برقرار می‌کند (قسمت 8). تمام این موارد در پروژه‌ی این سری بررسی شده‌اند و حتما نیاز هست یکبار قدم به قدم بررسی شود.
- در قسمت سوم این مورد به تفصیل بررسی شده.