مطالب
تولید هدرهای Content Security Policy توسط ASP.NET Core برای برنامه‌های Angular
پیشتر مطلب «افزودن هدرهای Content Security Policy به برنامه‌های ASP.NET» را در این سایت مطالعه کرده‌اید. در اینجا قصد داریم معادل آن‌را برای ASP.NET Core تهیه کرده و همچنین نکات مرتبط با برنامه‌های Angular را نیز در آن لحاظ کنیم.


تهیه میان افزار افزودن هدرهای Content Security Policy

کدهای کامل این میان افزار را در ادامه مشاهده می‌کنید:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

namespace AngularTemplateDrivenFormsLab.Utils
{
    public class ContentSecurityPolicyMiddleware
    {
        private readonly RequestDelegate _next;

        public ContentSecurityPolicyMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public Task Invoke(HttpContext context)
        {
            context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
            context.Response.Headers.Add("X-Xss-Protection", "1; mode=block");
            context.Response.Headers.Add("X-Content-Type-Options", "nosniff");

            string[] csp =
            {
              "default-src 'self'",
              "style-src 'self' 'unsafe-inline'",
              "script-src 'self' 'unsafe-inline' 'unsafe-eval'",
              "font-src 'self'",
              "img-src 'self' data:",
              "connect-src 'self'",
              "media-src 'self'",
              "object-src 'self'",
              "report-uri /api/CspReport/Log" //TODO: Add api/CspReport/Log
            };
            context.Response.Headers.Add("Content-Security-Policy", string.Join("; ", csp));
            return _next(context);
        }
    }

    public static class ContentSecurityPolicyMiddlewareExtensions
    {
        /// <summary>
        /// Make sure you add this code BEFORE app.UseStaticFiles();,
        /// otherwise the headers will not be applied to your static files.
        /// </summary>
        public static IApplicationBuilder UseContentSecurityPolicy(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<ContentSecurityPolicyMiddleware>();
        }
    }
}
که نحوه‌ی استفاده از آن در کلاس آغازین برنامه به صورت ذیل خواهد بود:
public void Configure(IApplicationBuilder app)
{
   app.UseContentSecurityPolicy();

توضیحات تکمیلی

افزودن X-Frame-Options
 context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
از هدر X-FRAME-OPTIONS، جهت منع نمایش و رندر سایت جاری، در iframeهای سایت‌های دیگر استفاده می‌شود. ذکر مقدار SAMEORIGIN آن، به معنای مجاز تلقی کردن دومین جاری برنامه است.


افزودن X-Xss-Protection
 context.Response.Headers.Add("X-Xss-Protection", "1; mode=block");
تقریبا تمام مرورگرهای امروزی قابلیت تشخیص حملات XSS را توسط static analysis توکار خود دارند. این هدر، آنالیز اجباری XSS را فعال کرده و همچنین تنظیم حالت آن به block، نمایش و رندر قسمت مشکل‌دار را به طور کامل غیرفعال می‌کند.


افزودن X-Content-Type-Options
 context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
وجود این هدر سبب می‌شود تا مرورگر، حدس‌زدن نوع فایل‌ها، درخواست‌ها و محتوا را کنار گذاشته و صرفا به content-type ارسالی توسط سرور اکتفا کند. به این ترتیب برای مثال امکان لینک کردن یک فایل غیرجاوا اسکریپتی و اجرای آن به صورت کدهای جاوا اسکریپت، چون توسط تگ script ذکر شده‌است، غیرفعال می‌شود. در غیراینصورت مرورگر هرچیزی را که توسط تگ script به صفحه لینک شده باشد، صرف نظر از content-type واقعی آن، اجرا خواهد کرد.


افزودن Content-Security-Policy
string[] csp =
            {
              "default-src 'self'",
              "style-src 'self' 'unsafe-inline'",
              "script-src 'self' 'unsafe-inline' 'unsafe-eval'",
              "font-src 'self'",
              "img-src 'self' data:",
              "connect-src 'self'",
              "media-src 'self'",
              "object-src 'self'",
              "report-uri /api/CspReport/Log" //TODO: Add api/CspReport/Log
            };
context.Response.Headers.Add("Content-Security-Policy", string.Join("; ", csp));
وجود این هدر، تزریق کدها و منابع را از دومین‌های دیگر غیرممکن می‌کند. برای مثال ذکر self در اینجا به معنای مجاز بودن الصاق و اجرای اسکریپت‌ها، شیوه‌نامه‌ها، تصاویر و اشیاء، صرفا از طریق دومین جاری برنامه است و هرگونه منبعی که از دومین‌های دیگر به برنامه تزریق شود، قابلیت اجرایی و یا نمایشی نخواهد داشت.

در اینجا ذکر unsafe-inline و unsafe-eval را مشاهده می‌کنید. برنامه‌های Angular به همراه شیوه‌نامه‌های inline و یا بکارگیری متد eval در مواردی خاص هستند. اگر این دو گزینه ذکر و فعال نشوند، در کنسول developer مرورگر، خطای بلاک شدن آن‌ها را مشاهده کرده و همچنین برنامه از کار خواهد افتاد.

یک نکته: با فعالسازی گزینه‌ی aot-- در حین ساخت برنامه، می‌توان unsafe-eval را نیز حذف کرد.


استفاده از فایل web.config برای تعریف SameSite Cookies

یکی از پیشنهادهای اخیر ارائه شده‌ی جهت مقابله‌ی با حملات CSRF و XSRF، قابلیتی است به نام  Same-Site Cookies. به این ترتیب مرورگر، کوکی سایت جاری را به همراه یک درخواست ارسال آن به سایت دیگر، پیوست نمی‌کند (کاری که هم اکنون با درخواست‌های Cross-Site صورت می‌گیرد). برای رفع این مشکل، با این پیشنهاد امنیتی جدید، تنها کافی است SameSite، به انتهای کوکی اضافه شود:
 Set-Cookie: sess=abc123; path=/; SameSite

نگارش‌های بعدی ASP.NET Core، ویژگی SameSite را نیز به عنوان CookieOptions لحاظ کرده‌اند. همچنین یک سری از کوکی‌های خودکار تولیدی توسط آن مانند کوکی‌های anti-forgery به صورت خودکار با این ویژگی تولید می‌شوند.
اما مدیریت این مورد برای اعمال سراسری آن، با کدنویسی میسر نیست (مگر اینکه مانند نگارش‌های بعدی ASP.NET Core پشتیبانی توکاری از آن صورت گیرد). به همین جهت می‌توان از ماژول URL rewrite مربوط به IIS برای افزودن ویژگی SameSite به تمام کوکی‌های تولید شده‌ی توسط سایت، کمک گرفت. برای این منظور تنها کافی است فایل web.config را ویرایش کرده و موارد ذیل را به آن اضافه کنید:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <outboundRules>
        <clear />
        <!-- https://scotthelme.co.uk/csrf-is-dead/ -->
        <rule name="Add SameSite" preCondition="No SameSite">
          <match serverVariable="RESPONSE_Set_Cookie" pattern=".*" negate="false" />
          <action type="Rewrite" value="{R:0}; SameSite=lax" />
          <conditions></conditions>
        </rule>
        <preConditions>
          <preCondition name="No SameSite">
            <add input="{RESPONSE_Set_Cookie}" pattern="." />
            <add input="{RESPONSE_Set_Cookie}" pattern="; SameSite=lax" negate="true" />
          </preCondition>
        </preConditions>
      </outboundRules>
    </rewrite>
  </system.webServer>
</configuration>


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

اگر به هدر Content-Security-Policy دقت کنید، گزینه‌ی آخر آن، ذکر اکشن متدی در سمت سرور است:
   "report-uri /api/CspReport/Log" //TODO: Add api/CspReport/Log
با تنظیم این مورد، می‌توان موارد بلاک شده را در سمت سرور لاگ کرد. اما این اطلاعات ارسالی به سمت سرور، فرمت خاصی را دارند:
{
  "csp-report": {
    "document-uri": "http://localhost:5000/untypedSha",
    "referrer": "",
    "violated-directive": "script-src",
    "effective-directive": "script-src",
    "original-policy": "default-src 'self'; style-src 'self'; script-src 'self'; font-src 'self'; img-src 'self' data:; connect-src 'self'; media-src 'self'; object-src 'self'; report-uri /api/Home/CspReport",
    "disposition": "enforce",
    "blocked-uri": "eval",
    "line-number": 21,
    "column-number": 8,
    "source-file": "http://localhost:5000/scripts.bundle.js",
    "status-code": 200,
    "script-sample": ""
  }
}
به همین جهت ابتدا نیاز است توسط JsonProperty کتابخانه‌ی JSON.NET، معادل این خواص را تولید کرد:
    class CspPost
    {
        [JsonProperty("csp-report")]
        public CspReport CspReport { get; set; }
    }

    class CspReport
    {
        [JsonProperty("document-uri")]
        public string DocumentUri { get; set; }

        [JsonProperty("referrer")]
        public string Referrer { get; set; }

        [JsonProperty("violated-directive")]
        public string ViolatedDirective { get; set; }

        [JsonProperty("effective-directive")]
        public string EffectiveDirective { get; set; }

        [JsonProperty("original-policy")]
        public string OriginalPolicy { get; set; }

        [JsonProperty("disposition")]
        public string Disposition { get; set; }

        [JsonProperty("blocked-uri")]
        public string BlockedUri { get; set; }

        [JsonProperty("line-number")]
        public int LineNumber { get; set; }

        [JsonProperty("column-number")]
        public int ColumnNumber { get; set; }

        [JsonProperty("source-file")]
        public string SourceFile { get; set; }

        [JsonProperty("status-code")]
        public string StatusCode { get; set; }

        [JsonProperty("script-sample")]
        public string ScriptSample { get; set; }
    }
اکنون می‌توان بدنه‌ی درخواست را استخراج و سپس به این شیء ویژه نگاشت کرد:
namespace AngularTemplateDrivenFormsLab.Controllers
{
    [Route("api/[controller]")]
    public class CspReportController : Controller
    {
        [HttpPost("[action]")]
        [IgnoreAntiforgeryToken]
        public async Task<IActionResult> Log()
        {
            CspPost cspPost;
            using (var bodyReader = new StreamReader(this.HttpContext.Request.Body))
            {
                var body = await bodyReader.ReadToEndAsync().ConfigureAwait(false);
                this.HttpContext.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body));
                cspPost = JsonConvert.DeserializeObject<CspPost>(body);
            }

            //TODO: log cspPost

            return Ok();
        }
    }
}
در اینجا نحوه‌ی استخراج Request.Body را به صورت خام را مشاهده می‌کنید. سپس توسط متد DeserializeObject کتابخانه‌ی JSON.NET، این رشته به شیء CspPost نگاشت شده‌است.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید.
مطالب
مروری بر کتابخانه ReactJS - قسمت دوم - نصب و پیکربندی React‌JS برای Visual Studio Code

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

var ClickableImage = function (props) {
    return (
        <a href={props.href}>
            <img src={props.src} />
        </a>
    )
};
میان یک فایل جاوااسکریپت، از تگ‌های HTML استفاده شده‌است. چرا و چطور؟! 

React از دو روش برای ساخت تگ‌ها استفاده میکند. روش ساده‌تر همین مثالی است که در بالا آمده؛ یعنی از تگ‌های HTML را به صورت مستقیم استفاده می‌کند. روش دیگر، استفاده از زبان جاوااسکریپت به تنهایی است. مثلا تگ‌های <a> و <img>  بالا به صورت زیر نوشته میشوند:

var ClickableImage = function(props) {
    React.createElement(
        "a", 
        {href: props.href}, 
        React.createElement(
            "img",
            {src: props.src}
        )
    )
};

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


JSX

JSX زبان یا روشی است که اجازه میدهد از تگ‌های مشابه با HTML در جاوااسکریپت استفاده کنیم. به دلیل تفاوت‌های مختصری که دارند، گفته شد که این تگ‌ها دقیقا HTML نیستند. برای مثال در تگ‌های HTML خاصیت‌های class و for را داریم؛ اما در تگ‌های JSX باید به ترتیب از className و htmlFor استفاده کنیم. مسئله بعدی این است که اساسا JSX همراه با React ارائه نشده و برای اینکه بتوانیم از JSX استفاده کنیم، نیاز به ابزاری اضافه داریم تا JSX را برای JavaScript و مرورگر ترجمه کند که فیسبوک، Babel را پیشنهاد میدهد. اگر از JSX بدون ابزار مترجم استفاده کنیم با پیام خطای زیر مواجه می‌شویم. 

  > Uncaught SyntaxError: Unexpected token

یعنی کاراکتر شروع (>) تگ‌ها را تشخیص نمیدهد.


نصب کتابخانه‌ها

این کتابخانه‌ها را می‌شود به مدل‌های مختلفی دریافت و پیکربندی کرد. بسته به نوع پروژه و محیط توسعه و پیکربندی‌های خاص هر پروژه، روش کار میتواند متفاوت باشد. هدف اصلی، مروری بر امکانات React است. پس ساده‌ترین روش نصب را برای ادامه کار انتخاب میکنیم. مانند هر کتابخانه‌ی دیگری میشود بطور یکجا React و Babel را از سایت‌های اصلی یا Github دانلود و به پروژه اضافه و استفاده کرد؛ مثل jQuery و Bootstrap. اما راه استاندارد و پیشنهاد شده، استفاده از ابزارهای مدیریت بسته‌ها مثل npm است. در قدم اول با فرض بر اینکه VSCode و npm بر روی سیستم نصب هستند، اول یک پوشه خالی را در VSCode به عنوان پروژه باز میکنیم و از منوی View -> Integrated Terminal  را انتخاب میکنیم. در ترمینال باز شده دستور نصب زیر را وارد میکنیم.

npm install react react-dom
با این کار پوشه node_modules به ریشه پروژه اضافه میشود که حاوی کتابخانه React است. پوشه node_modules مختص به React نیست. هر کتابخانه‌ای را که به این صورت نصب کنیم، به این پوشه اضافه میشود. مرحله بعد، نصب کتابخانه Babel جهت استفاده‌ی از JSX است. کتابخانه Babel یک مترجم بزرگ است که اجزای مختلفی دارد. ما به حداقل‌هایی از آن برای ترجمه تگ‌های JSX  احتیاج داریم. برای این کار همانند قبل، ترمینال را انتخاب میکنیم و دستور نصب زیر را اجرا میکنیم. با این دستور نصب، کتابخانه مختصر babel-standalone به پوشه node_modules اضافه میشود.
npm install babel-standalone

نحوه استفاده

فایل index.html را به ریشه پروژه اضافه کنید و کدهای زیر را درون تگ Body قرار دهید: 

<div id="reactTestContainer"></div>

<script src="node_modules/react/dist/react.min.js"></script>
<script src="node_modules/react-dom/dist/react-dom.min.js"></script>
<script src="node_modules/babel-standalone/babel.min.js"></script>
<script src="react-test.js" type="text/babel"></script>

برای کار با کتابخانه React به دو فایل react.js و react-dom.js نیاز داریم. اولی بخش اصلی کتابخانه و دومی برای ساخت تگ‌ها و تبادل با بخش HTML DOM استفاده میشود؛ مثلا اضافه کردن تگ‌های ساخته شده به HTML. ذکر ویژگی "type="text/babel برای فایل‌هایی که در آنها از تگ‌های JSX استفاده شده ضروری است؛ در غیر اینصورت Babel  تشخیص نمیدهد که کار ترجمه را برای چه فایلهایی باید انجام دهد. در نهایت قطعه کد زیر را در فایل react-test.js وارد کنید. 

var ClickableImage = function (props) {
    return (
        <a href={props.href}>
            <img src={props.src} />
        </a>
    )
};

ReactDOM.render(
    <ClickableImage href="http://google.com" src="google.jpg"/>, 
    document.getElementById("reactTestContainer")
)

با اجرا کردن پروژه، تصویری قابل کلیک به مقصد گوگل، در تگ <div>، با ویژگی "id=”reactTestContainer ایجاد خواهد شد.

تا به این قسمت متوجه شدیم که کتابخانه React چیست و چطور میشود از آن استفاده کرد. در قسمت بعد نگاهی دقیق‌تر به کامپوننت‌های React خواهیم داشت.

مطالب
توزیع یک اپلیکیشن ASP.NET MVC 5 روی Windows Azure
این مقاله به شما نشان می‌دهد چگونه یک اپلیکیشن وب ASP.NET MVC 5 بسازید که کاربران را قادر می‌سازد با اطلاعات Facebook یا Google احراز هویت شده و به سایت وارد شوند. همچنین این اپلیکیشن را روی Windows Azure توزیع (Deploy) خواهید کرد.
می توانید بصورت رایگان یک حساب کاربری Windows Azure بسازید. اگر هم Visual Studio 2013 را ندارید، بسته SDK بصورت خودکار Visual Studio 2013 for Web را نصب می‌کند. پس از آن می‌توانید به توسعه رایگان اپلیکیشن‌های Azure بپردازید، اگر می‌خواهید از Visual Studio 2012 استفاده کنید به این مقاله مراجعه کنید. این مقاله نسبت به لینک مذکور بسیار ساده‌تر است.
این مقاله فرض را بر این می‌گذارد که شما هیچ تجربه ای در کار با Windows Azure ندارید. در انتهای این مقاله شما یک اپلیکیشن مبتنی بر داده (data-driven) و امن خواهید داشت که در فضای رایانش ابری اجرا می‌شود.
چیزی که شما یاد می‌گیرید:
  • چطور یک اپلیکیشن وب ASP.NET MVC 5 بسازید و آن را روی یک وب سایت Windows Azure منتشر کنید.
  • چگونه از OAuth، OpenID و سیستم عضویت ASP.NET برای ایمن سازی اپلیکیشن خود استفاده کنید.
  • چگونه از API جدید سیستم عضویت برای مدیریت اعضا و نقش‌ها استفاده کنید.
  • چگونه از یک دیتابیس SQL برای ذخیره داده‌ها در Windows Azure استفاده کنید.
شما یک اپلیکیشن مدیریت تماس (Contact Manager) ساده خواهید نوشت که بر پایه ASP.NET MVC 5 بوده و از Entity Framework برای دسترسی داده استفاده می‌کند. تصویر زیر صفحه ورود نهایی اپلیکیشن را نشان می‌دهد.

توجه: برای تمام کردن این مقاله به یک حساب کاربری Windows Azure نیاز دارید، که بصورت رایگان می‌توانید آن را بسازید. برای اطلاعات بیشتر به Windows Azure Free Trial مراجعه کنید.

در این مقاله:

  • برپایی محیط توسعه (development environment)
  • برپایی محیط Windows Azure
  • ایجاد یک اپلیکیشن ASP.NET MVC 5
  • توزیع اپلیکیشن روی Windows Azure
  • افزودن یک دیتابیس به اپلیکیشن
  • افزودن یک OAuth Provider
  • استفاده از Membership API
  • توزیع اپلیکیشن روی Windows Azure
  • قدم‌های بعدی


برپایی محیط توسعه

برای شروع Windows Azure SDK for .NET را نصب کنید. برای اطلاعات بیشتر به Windows Azure SDK for Visual Studio 2013 مراجعه کنید. بسته به اینکه کدام یک از وابستگی‌ها را روی سیستم خود دارید، پروسه نصب می‌تواند از چند دقیقه تا نزدیک دو ساعت طول بکشد. توسط Web Platform می‌توانید تمام نیازمندی‌های خود را نصب کنید.

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


برپایی محیط Windows Azure

در قدم بعدی باید یک وب سایت Windows Azure و یک دیتابیس بسازیم.
ایجاد یک وب سایت و دیتابیس در Windows Azure

وب سایت Windows Azure شما در یک محیط اشتراکی (shared) میزبانی می‌شود، و این بدین معنا است که وب سایت‌های شما روی ماشین‌های مجازی (virtual machines) اجرا می‌شوند که با مشتریان دیگر Windows Azure به اشتراک گذاشته شده اند. یک محیط میزبانی اشتراکی گزینه ای کم هزینه برای شروع کار با رایانش‌های ابری است. اگر در آینده ترافیک وب سایت شما رشد چشم گیری داشته باشد، می‌توانید اپلیکیشن خود را طوری توسعه دهید که به نیازهای جدید پاسخگو باشد و آن را روی یک ماشین مجازی اختصاصی (dedicated VMs) میزبانی کنید. اگر معماری پیچیده‌تری نیاز دارید، می‌توانید به یک سرویس Windows Azure Cloud مهاجرت کنید. سرویس‌های ابری روی ماشین‌های مجازی اختصاصی اجرا می‌شوند که شما می‌توانید تنظیمات آنها را بر اساس نیازهای خود پیکربندی کنید.
Windows Azure SQL Database یک سرویس دیتابیس رابطه ای (relational) و مبتنی بر Cloud است که بر اساس تکنولوژی‌های SQL Server ساخته شده. ابزار و اپلیکیشن هایی که با SQL Server کار می‌کنند با SQL Database نیز می‌توانند کار کنند.

  • روی Web Site  و سپس Custom Create  کلیک کنید.

  • در مرحله Create Web Site  در قسمت URL  یک رشته وارد کنید که آدرسی منحصر بفرد برای اپلیکیشن شما خواهد بود. آدرس کامل وب سایت شما، ترکیبی از مقدار این فیلد و مقدار روبروی آن است.

  • در لیست Database گزینه Create  a free 20 MB SQL Database  را انتخاب کنید.
  • در لیست Region  همان مقداری را انتخاب کنید که برای وب سایت تان انتخاب کرده اید. تنظیمات این قسمت مشخص می‌کند که ماشین مجازی (VM) شما در کدام مرکز داده (data center) خواهد بود.
  • در قسمت DB Connection String Name  مقدار پیش فرض DefaultConnection  را بپذیرید.
  • دکمه فلش پایین صفحه را کلیک کنید تا به مرحله بعد، یعنی مرحله Specify Database Settings  بروید.
  • در قسمت Name  مقدار ContactDB  را وارد کنید (تصویر زیر).
  • در قسمت Server  گزینه New SQL Database Server  را انتخاب کنید. اگر قبلا دیتابیس ساخته اید می‌توانید آن را از کنترل dropdown انتخاب کنید.
  • مقدار قسمت Region  را به همان مقداری که برای ایجاد وب سایت تان تنظیم کرده اید تغییر دهید.
  • یک Login Name  و Password  مدیر (administrator) وارد کنید. اگر گزینه  New SQL Database server را انتخاب کرده اید، چنین کاربری وجود ندارد و در واقع اطلاعات یک حساب کاربری جدید را وارد می‌کنید تا بعدا هنگام دسترسی به دیتابیس از آن استفاده کنید. اگر دیتابیس دیگری را از لیست انتخاب کرده باشید، اطلاعات یک حساب کاربری موجود از شما دریافت خواهد شد. در مثال این مقاله ما گزینه Advanced  را رها می‌کنیم. همچنین در نظر داشته باشید که برای دیتابیس‌های رایگان تنها از یک Collation می‌توانید استفاده کنید.

دکمه تایید پایین صفحه را کلیک کنید تا مراحل تمام شود.

تصویر زیر استفاده از یک SQL Server و حساب کاربری موجود (existing) را نشان می‌دهد.

پرتال مدیریتی پس از اتمام مراحل، به صفحه وب سایت‌ها باز می‌گردد. ستون Status نشان می‌دهد که سایت شما در حال ساخته شدن است. پس از مدتی (معمولا کمتر از یک دقیقه) این ستون نشان می‌دهد که سایت شما با موفقیت ایجاد شده. در منوی پیمایش سمت چپ، تعداد سایت هایی که در اکانت خود دارید در کنار آیکون Web Sites نمایش داده شده است، تعداد دیتابیس‌ها نیز در کنار آیکون SQL Databases نمایش داده می‌شود.


یک اپلیکیشن ASP.NET MVC 5 بسازید

شما یک وب سایت Windows Azure ساختید، اما هنوز هیچ محتوایی در آن وجود ندارد. قدم بعدی ایجاد یک اپلیکیشن وب در ویژوال استودیو و انتشار آن است. ابتدا یک پروژه جدید بسازید.

نوع پروژه را ASP.NET Web Application انتخاب کنید.

نکته: در تصویر بالا نام پروژه "MyExample" است اما حتما نام پروژه خود را به "ContactManager" تغییر دهید. قطعه کدهایی که در ادامه مقاله خواهید دید نام پروژه را ContactManager فرض می‌کنند.

در دیالوگ جدید ASP.NET نوع اپلیکیشن را MVC انتخاب کنید و دکمه Change Authentication را کلیک کنید.

گزینه پیش فرض Individual User Accounts را بپذیرید. برای اطلاعات بیشتر درباره متدهای دیگر احراز هویت به این لینک مراجعه کنید. دکمه‌های OK را کلیک کنید تا تمام مراحل تمام شوند.


تنظیم تیتر و پاورقی سایت

  • فایل Layout.cshtml_   را باز کنید. دو نمونه از متن "My ASP.NET MVC Application" را با عبارت "Contact Manager" جایگزین کنید.
  • عبارت "Application name" را هم با "CM Demo" جایگزین کنید.
اولین Action Link را ویرایش کنید و مقدار Home را با Cm جایگزین کنید تا از CmController استفاده کند.


اپلیکیشن را بصورت محلی اجرا کنید

اپلیکیشن را با Ctrl + F5 اجرا کنید. صفحه اصلی باید در مرورگر پیش فرض باز شود.

اپلیکیشن شما فعلا آماده است و می‌توانید آن را روی Windows Azure توزیع کنید. بعدا دیتابیس و دسترسی داده نیز اضافه خواهد شد.


اپلیکیشن را روی Windows Azure منتشر کنید

در ویژوال استودیو روی نام پروژه کلیک راست کنید و گزینه Publish را انتخاب کنید. ویزارد Publish Web باز می‌شود.
در قسمت Profile روی Import کلیک کنید.

حال دیالوگ Import Publish Profile نمایش داده می‌شود.

یکی از متدهای زیر را استفاده کنید تا ویژوال استودیو بتواند به اکانت Windows Azure شما متصل شود.

  • روی Sign In کلیک کنید تا با وارد کردن اطلاعات حساب کاربری وارد Windows Azure شوید.
این روش ساده‌تر و سریع‌تر است، اما اگر از آن استفاده کنید دیگر قادر به مشاهده Windows Azure SQL Database یا Mobile Services در پنجره Server Explorer نخواهید بود.
  • روی Manage subscriptions کلیک کنید تا یک management certificate نصب کنید، که دسترسی به حساب کاربری شما را ممکن می‌سازد.
در دیالوگ باکس Manage Windows Azure Subscriptions به قسمت Certificates بروید. سپس Import را کلیک کنید. مراحل را دنبال کنید تا یک فایل subscription را بصورت دانلود دریافت کنید (فایل‌های publishsettings.) که اطلاعات اکانت Windows Azure شما را دارد.

نکته امنیتی: این فایل تنظیمات را بیرون از پوشه‌های سورس کد خود دانلود کنید، مثلا پوشه Downloads. پس از اتمام عملیات Import هم این فایل را حذف کنید. کاربر مخربی که به این فایل دسترسی پیدا کند قادر خواهد بود تا سرویس‌های Windows Azure شما را کاملا کنترل کند.
برای اطلاعات بیشتر به How to Connect to Windows Azure from Visual Studio مراجعه کنید.
در دیالوگ باکس Import Publish Profile وب سایت خود را از لیست انتخاب کنید و OK را کلیک کنید.

در دیالوگ باکس Publish Web روی Publish کلیک کنید.

اپلیکیشن شما حالا در فضای ابری اجرا می‌شود. دفعه بعد که اپلیکیشن را منتشر کنید تنها فایل‌های تغییر کرده (یا جدید) آپلود خواهند شد.


یک دیتابیس به اپلیکیشن اضافه کنید

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


کلاس‌های مدل Contacts را اضافه کنید

در پوشه Models پروژه یک کلاس جدید ایجاد کنید.

نام کلاس را به Contact.cs تغییر دهید و دکمه Add را کلیک کنید.

کد فایل Contact.cs را با قطعه کد زیر مطابقت دهید.

using System.ComponentModel.DataAnnotations;
using System.Globalization;
namespace ContactManager.Models
{
    public class Contact
    {
        public int ContactId { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Zip { get; set; }
        [DataType(DataType.EmailAddress)]
        public string Email { get; set; }
    }
}

این کلاس موجودیت Contact را در دیتابیس معرفی می‌کند. داده هایی که می‌خواهیم برای هر رکورد ذخیره کنیم تعریف شده اند، بعلاوه یک فیلد Primary Key که دیتابیس به آن نیاز دارد.


یک کنترلر و نما برای داده‌ها اضافه کنید

ابتدا پروژه را Build کنید (Ctrl + Shift+ B). این کار را باید پیش از استفاده از مکانیزم Scaffolding انجام دهید.
یک کنترلر جدید به پوشه Controllers اضافه کنید.

در دیالوگ باکس Add Scaffold گزینه MVC 5 Controller with views, using EF را انتخاب کنید.

در دیالوگ Add Controller نام "CmController" را برای کنترلر وارد کنید. (تصویر زیر.)

در لیست Model گزینه (Contact (ContactManager.Models را انتخاب کنید.

در قسمت Data context class گزینه (ApplicationDbContext (ContactManager.Models را انتخاب کنید. این ApplicationDbContext هم برای اطلاعات سیستم عضویت و هم برای داده‌های Contacts استفاده خواهد شد.

روی Add کلیک کنید. ویژوال استودیو بصورت خودکار با استفاده از Scaffolding متدها و View‌های لازم برای عملیات CRUD را فراهم می‌کند، که همگی از مدل Contact استفاده می‌کنند.


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

مرحله بعدی فعال کردن قابلیت Code First Migrations است تا دیتابیس را بر اساس الگویی که تعریف کرده اید بسازد.
از منوی Tools گزینه Library Package Manager و سپس Package Manager Console را انتخاب کنید.

در پنجره باز شده فرمان زیر را وارد کنید.

enable-migrations

فرمان enable-migrations یک پوشه با نام Migrations می سازد و فایلی با نام Configuration.cs را به آن اضافه می‌کند. با استفاده از این کلاس می‌توانید داده‌های اولیه دیتابیس را وارد کنید و مهاجرت‌ها را نیز پیکربندی کنید.

در پنجره Package Manager Console فرمان زیر را وارد کنید.

add-migration Initial

فرمان add-migration initial فایلی با نام data_stamp> initial> ساخته و آن را در پوشه Migrations ذخیره می‌کند. در این مرحله دیتابیس شما ایجاد می‌شود. در این فرمان، مقدار initial اختیاری است و صرفا برای نامگذاری فایل مهاجرت استفاده شده. فایل‌های جدید را می‌توانید در Solution Explorer مشاهده کنید.

در کلاس Initial متد Up جدول Contacts را می‌سازد. و متد Down (هنگامی که می‌خواهید به وضعیت قبلی بازگردید) آن را drop می‌کند.

حال فایل Migrations/Configuration.cs را باز کنید. فضای نام زیر را اضافه کنید.

using ContactManager.Models;

حال متد Seed را با قطعه کد زیر جایگزین کنید.

protected override void Seed(ContactManager.Models.ApplicationDbContext context)
{
    context.Contacts.AddOrUpdate(p => p.Name,
       new Contact
       {
           Name = "Debra Garcia",
           Address = "1234 Main St",
           City = "Redmond",
           State = "WA",
           Zip = "10999",
           Email = "debra@example.com",
       },
        new Contact
        {
            Name = "Thorsten Weinrich",
            Address = "5678 1st Ave W",
            City = "Redmond",
            State = "WA",
            Zip = "10999",
            Email = "thorsten@example.com",
        },
        new Contact
        {
            Name = "Yuhong Li",
            Address = "9012 State st",
            City = "Redmond",
            State = "WA",
            Zip = "10999",
            Email = "yuhong@example.com",
        },
        new Contact
        {
            Name = "Jon Orton",
            Address = "3456 Maple St",
            City = "Redmond",
            State = "WA",
            Zip = "10999",
            Email = "jon@example.com",
        },
        new Contact
        {
            Name = "Diliana Alexieva-Bosseva",
            Address = "7890 2nd Ave E",
            City = "Redmond",
            State = "WA",
            Zip = "10999",
            Email = "diliana@example.com",
        }
        );
}

این متد دیتابیس را Seed می‌کند، یعنی داده‌های پیش فرض و اولیه دیتابیس را تعریف می‌کند. برای اطلاعات بیشتر به Seeding and Debugging Entity Framework (EF) DBs مراجعه کنید.

در پنجره Package Manager Console فرمان زیر را وارد کنید.

update-database

فرمان update-database مهاجرت نخست را اجرا می‌کند، که دیتابیس را می‌سازد. بصورت پیش فرض این یک دیتابیس SQL Server Express LocalDB است.

حال پروژه را با CTRL + F5 اجرا کنید.

همانطور که مشاهده می‌کنید، اپلیکیشن داده‌های اولیه (Seed) را نمایش می‌دهد، و لینک هایی هم برای ویرایش، حذف و مشاهده جزئیات رکورد‌ها فراهم می‌کند. می‌توانید داده‌ها را مشاهده کنید، رکورد جدید ثبت کنید و یا داده‌های قبلی را ویرایش و حذف کنید.


یک تامین کننده OAuth2 و OpenID اضافه کنید

OAuth یک پروتکل باز است که امکان authorization امن توسط یک متد استاندارد را فراهم می‌کند. این پروتکل می‌تواند در اپلیکیشن‌های وب، موبایل و دسکتاپ استفاده شود. قالب پروژه ASP.NET MVC internet از OAuth و OpenID استفاده می‌کند تا فیسبوک، توییتر، گوگل و حساب‌های کاربری مایکروسافت را بعنوان تامین کنندگان خارجی تعریف کند. به سادگی می‌توانید قطعه کدی را ویرایش کنید و از تامین کننده احراز هویت مورد نظرتان استفاده کنید. مراحلی که برای اضافه کردن این تامین کنندگان باید دنبال کنید، بسیار مشابه همین مراحلی است که در این مقاله دنبال خواهید کرد. برای اطلاعات بیشتر درباره نحوه استفاده از فیسبوک بعنوان یک تامین کننده احراز هویت به Create an ASP.NET MVC 5 App with Facebook and Google OAuth2 and OpenID Sign-on مراجعه کنید.
علاوه بر احراز هویت، اپلیکیشن ما از نقش‌ها (roles) نیز استفاده خواهد کرد تا از authorization پشتیبانی کند. تنها کاربرانی که به نقش canEdit تعلق داشته باشند قادر به ویرایش اطلاعات خواهند بود (یعنی ایجاد، ویرایش و حذف رکورد ها).
فایل App_Start/Startup.Auth.cs را باز کنید. توضیحات متد app.UseGoogleAuthentication را حذف کنید.
حال اپلیکیشن را اجرا کنید و روی لینک Log In کلیک کنید.
زیر قسمت User another service to log in روی دکمه Google کلیک کنید. اطلاعات کاربری خود را وارد کنید. سپس Accept را کلیک کنید تا به اپلیکیشن خود دسترسی کافی بدهید (برای آدرس ایمیل و اطلاعات پایه).
حال باید به صفحه ثبت نام (Register) هدایت شوید. در این مرحله می‌توانید در صورت لزوم نام کاربری خود را تغییر دهید. نهایتا روی Register کلیک کنید.


استفاده از Membership API

در این قسمت شما یک کاربر محلی و نقش canEdit را به دیتابیس عضویت اضافه می‌کنید. تنها کاربرانی که به این نقش تعلق دارند قادر به ویرایش داده‌ها خواهند بود. یکی از بهترین تمرین‌ها (best practice) نام گذاری نقش‌ها بر اساس عملیاتی است که می‌توانند اجرا کنند. بنابراین مثلا canEdit نسبت به نقشی با نام admin ترجیح داده می‌شود. هنگامی که اپلیکیشن شما رشد می‌کند و بزرگتر می‌شود، شما می‌توانید نقش‌های جدیدی مانند canDeleteMembers اضافه کنید، بجای آنکه از نام‌های گنگی مانند superAdmin استفاده کنید.
فایل Migrations/Configuration.cs را باز کنید و عبارات زیر را به آن اضافه کنید.
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
متد AddUserAndRole را به این کلاس اضافه کنید.
bool AddUserAndRole(ContactManager.Models.ApplicationDbContext context)
 {
    IdentityResult ir;
    var rm = new RoleManager<IdentityRole>
        (new RoleStore<IdentityRole>(context));
    ir = rm.Create(new IdentityRole("canEdit"));
    var um = new UserManager<ApplicationUser>(
        new UserStore<ApplicationUser>(context));
    var user = new ApplicationUser()
    {
       UserName = "user1",
    };
    ir = um.Create(user, "Passw0rd1");
    if (ir.Succeeded == false)
       return ir.Succeeded;
    ir = um.AddToRole(user.Id, "canEdit");
    return ir.Succeeded;
 }
حالا از متد Seed این متد جدید را فراخوانی کنید.
protected override void Seed(ContactManager.Models.ApplicationDbContext context)
{
    AddUserAndRole(context);
    context.Contacts.AddOrUpdate(p => p.Name,
        // Code removed for brevity
}
این کدها نقش جدیدی با نام canEdit و کاربری با نام user1 می سازد. سپس این کاربر به نقش مذکور اضافه می‌شود.


کدی موقتی برای تخصیص نقش canEdit به کاربران جدید Social Provider ها

در این قسمت شما متد ExternalLoginConfirmation در کنترلر Account را ویرایش خواهید کرد. یا این تغییرات، کاربران جدیدی که توسط OAuth یا OpenID ثبت نام می‌کنند به نقش  canEdit اضافه می‌شوند. تا زمانی که ابزاری برای افزودن و مدیریت نقش‌ها بسازیم، از این کد موقتی استفاده خواهیم کرد. تیم مایکروسافت امیدوار است ابزاری مانند WSAT برای مدیریت کاربران و نقش‌ها در آینده عرضه کند. بعدا در این مقاله با اضافه کردن کاربران به نقش‌ها بصورت دستی از طریق Server Explorer نیز آشنا خواهید شد.
فایل Controllers/AccountController.cs را باز کنید و متد ExternalLoginConfirmation را پیدا کنید.
درست قبل از فراخوانی SignInAsync متد AddToRoleAsync را فراخوانی کنید.
await UserManager.AddToRoleAsync(user.Id, "CanEdit");
کد بالا کاربر ایجاد شده جدید را به نقش canEdit اضافه می‌کند، که به آنها دسترسی به متدهای ویرایش داده را می‌دهد. تصویری از تغییرات کد در زیر آمده است.

در ادامه مقاله اپلیکیشن خود را روی Windows Azure منتشر خواهید کرد و با استفاده از Google و تامین کنندگان دیگر وارد سایت می‌شوید. هر فردی که به آدرس سایت شما دسترسی داشته باشد، و یک حساب کاربری Google هم در اختیار داشته باشد می‌تواند در سایت شما ثبت نام کند و سپس دیتابیس را ویرایش کند. برای جلوگیری از دسترسی دیگران، می‌توانید وب سایت خود را متوقف (stop) کنید.

در پنجره Package Manager Console فرمان زیر را وارد کنید.

Update-Database

فرمان را اجرا کنید تا متد Seed را فراخوانی کند. حال AddUserAndRole شما نیز اجرا می‌شود. تا این مرحله نقش canEdit ساخته شده و کاربر جدیدی با نام user1 ایجاد و به آن افزوده شده است.


محافظت از اپلیکیشن توسط SSL و خاصیت Authorize

در این قسمت شما با استفاده از خاصیت Authorize دسترسی به اکشن متدها را محدود می‌کنید. کاربران ناشناس (Anonymous) تنها قادر به مشاهده متد Index در کنترلر home خواهند بود. کاربرانی که ثبت نام کرده اند به متدهای Index و Details در کنترلر Cm و صفحات About و Contact نیز دسترسی خواهند داشت. همچنین دسترسی به متدهایی که داده‌ها را تغییر می‌دهند تنها برای کاربرانی وجود دارد که در نقش canEdit هستند.

خاصیت Authorize و RequireHttps را به اپلیکیشن اضافه کنید. یک راه دیگر افزودن این خاصیت‌ها به تمام کنترلر‌ها است، اما تجارب امنیتی توصیه می‌کند که این خاصیت‌ها روی کل اپلیکیشن اعمال شوند. با افزودن این خاصیت‌ها بصورت global تمام کنترلر‌ها و اکشن متدهایی که می‌سازید بصورت خودکار محافظت خواهند شد، و دیگر لازم نیست بیاد داشته باشید کدام کنترلر‌ها و متدها را باید ایمن کنید.

برای اطلاعات بیشتر به Securing your ASP.NET MVC App and the new AllowAnonymous Attribute مراجعه کنید.

فایل App_Start/FilterConfig.cs را باز کنید و متد RegisterGlobalFilters را با کد زیر مطابقت دهید.

public static void
RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
    filters.Add(new System.Web.Mvc.AuthorizeAttribute());
    filters.Add(new RequireHttpsAttribute());
}

خاصیت Authorize در کد بالا از دسترسی کاربران ناشناس به تمام متدهای اپلیکیشن جلوگیری می‌کند. شما برای اعطای دسترسی به متدهایی خاص از خاصیت AllowAnonymous استفاده خواهید کرد. در آخر خاصیت RequireHTTPS باعث می‌شود تا تمام دسترسی‌ها به اپلیکیشن وب شما از طریق HTTPS صورت گیرد.

حالا خاصیت AllowAnonymous را به متد Index  در کنترلر Home اضافه کنید. از این خاصیت برای اعطای دسترسی به تمامی کاربران سایت استفاده کنید. قسمتی از کد کنترلر Home را در زیر می‌بینید.

namespace ContactManager.Controllers
 {
    public class HomeController : Controller
    {
       [AllowAnonymous]
       public ActionResult Index()
       {
          return View();
       }

یک جستجوی عمومی برای عبارت AllowAnonymous انجام دهید. همانطور که مشاهده می‌کنید این خاصیت توسط متدهای ورود و ثبت نام در کنترلر Account نیز استفاده شده است.

در کنترلر CmController خاصیت [("Authorize(Roles="canEdit] را به تمام متدهایی که با داده سر و کار دارند اضافه کنید، به غیر از متدهای Index و Details. قسمتی از کد کامل شده در زیر آمده است.


فعال سازی SSL برای پروژه

در Solution Explorer پروژه خود را انتخاب کنید. سپس کلید F4 را فشار دهید تا دیالوگ خواص (Properties) باز شود. حال مقدار خاصیت SSL Enabled را به true تنظیم کنید. آدرس SSL URL را کپی کنید. این آدرس چیزی شبیه به /https://localhost:44300 خواهد بود.

روی نام پروژه کلیک راست کنید و Properties را انتخاب کنید. در قسمت چپ گزینه Web را انتخاب کنید. حالا مقدار Project Url را به آدرسی که کپی کرده اید تغییر دهید. نهایتا تغییرات را ذخیره کنید و پنجره را ببندید.

حال پروژه را اجرا کنید. مرورگر شما باید یک پیام خطای اعتبارسنجی به شما بدهد. دلیلش این است که اپلیکیشن شما از یک Valid Certificate استفاده نمی‌کند. هنگامی که پروژه را روی Windows Azure منتشر کنید دیگر این پیغام را نخواهید دید. چرا که سرور‌های مایکروسافت همگی لایسنس‌های معتبری دارند. برای اپلیکیشن ما می‌توانید روی Continue to this website را انتخاب کنید.

حال مرورگر پیش فرض شما باید صفحه Index از کنترلر home را به شما نمایش دهد.

اگر از یک نشست قبلی هنوز در سایت هستید (logged-in) روی لینک Log out کلیک کنید و از سایت خارج شوید.

روی لینک‌های About و Contact کلیک کنید. باید به صفحه ورود به سایت هدایت شوید چرا که کاربران ناشناس اجازه دسترسی به این صفحات را ندارند.

روی لینک Register کلیک کنید و یک کاربر محلی با نام Joe بسازید. حال مطمئن شوید که این کاربر به صفحات Home, About و Contact دسترسی دارد.

روی لینک CM Demo کلیک کنید و مطمئن شوید که داده‌ها را مشاهده می‌کنید.

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

با کاربر user1 که قبلا ساختید وارد سایت شوید. حال به صفحه ویرایشی که قبلا درخواست کرده بودید هدایت می‌شوید.

اگر نتوانستید با این کاربر به سایت وارد شوید، کلمه عبور را از سورس کد کپی کنید و مجددا امتحان کنید. اگر همچنان نتوانستید به سایت وارد شوید، جدول AspNetUsers را بررسی کنید تا مطمئن شوید کاربر user1 ساخته شده است. این مراحل را در ادامه مقاله خواهید دید.

در آخر اطمینان حاصل کنید که می‌توانید داده‌ها را تغییر دهید.


اپلیکیشن را روی Windows Azure منتشر کنید

ابتدا پروژه را Build کنید. سپس روی نام پروژه کلیک راست کرده و گزینه Publish را انتخاب کنید.

در دیالوگ باز شده روی قسمت Settings کلیک کنید. روی File Publish Options کلیک کنید تا بتوانید Remote connection string را برای ApplicationDbContext و دیتابیس ContactDB انتخاب کنید.

اگر ویژوال استودیو را پس از ساخت Publish profile بسته و دوباره باز کرده اید، ممکن است رشته اتصال را در لیست موجود نبینید. در چنین صورتی، بجای ویرایش پروفایل انتشار، یک پروفایل جدید بسازید. درست مانند مراحلی که پیشتر دنبال کردید.

زیر قسمت ContactManagerContext گزینه Execute Code First Migrations را انتخاب کنید.

حال Publish را کلیک کنید تا اپلیکیشن شما منتشر شود. با کاربر user1 وارد سایت شوید و بررسی کنید که می‌توانید داده‌ها را ویرایش کنید یا خیر.

حال از سایت خارج شوید و توسط یک اکانت Google یا Facebook وارد سایت شوید، که در این صورت نقش canEdit نیز به شما تعلق می‌گیرد.


برای جلوگیری از دسترسی دیگران، وب سایت را متوقف کنید

در Server Explorer به قسمت Web Sites بروید. حال روی هر نمونه از وب سایت‌ها کلیک راست کنید و گزینه Stop Web Site را انتخاب کنید.

یک راه دیگر متوقف کردن وب سایت از طریق پرتال مدیریت Windows Azure است.


فراخوانی AddToRoleAsync را حذف و اپلیکیشن را منتشر و تست کنید

کنترلر Account را باز کنید و کد زیر را از متد ExternalLoginConfirmation حذف کنید.
await UserManager.AddToRoleAsync(user.Id, "CanEdit");
پروژه را ذخیره و Build کنید. حال روی نام پروژه کلیک راست کرده و Publish را انتخاب کنید.

دکمه Start Preview را فشار دهید. در این مرحله تنها فایل هایی که نیاز به بروز رسانی دارند آپلود خواهند شد.

وب سایت را راه اندازی کنید. ساده‌ترین راه از طریق پرتال مدیریت Windows Azure است. توجه داشته باشید که تا هنگامی که وب سایت شما متوقف شده، نمی‌توانید اپلیکیشن خود را منتشر کنید.

حال به ویژوال استودیو بازگردید و اپلیکیشن را منتشر کنید. اپلیکیشن Windows Azure شما باید در مرورگر پیش فرض تان باز شود. حال شما در حال مشاهده صفحه اصلی سایت بعنوان یک کاربر ناشناس هستید.

روی لینک About کلیک کنید، که شما را به صفحه ورود هدایت می‌کند.

روی لینک Register در صفحه ورود کلیک کنید و یک حساب کاربری محلی بسازید. از این حساب کاربری برای این استفاده می‌کنیم که ببینیم شما به صفحات فقط خواندنی (read-only) و نه صفحاتی که داده‌ها را تغییر می‌دهند دسترسی دارید یا خیر. بعدا در ادامه مقاله، دسترسی حساب‌های کاربری محلی (local) را حذف می‌کنیم.

مطمئن شوید که به صفحات About و Contact دسترسی دارید.

لینک CM Demo را کلیک کنید تا به کنترلر CmController هدایت شوید. 

روی یکی از لینک‌های Edit کلیک کنید. این کار شما را به صفحه ورود به سایت هدایت می‌کند. در زیر قسمت User another service to log in یکی از گزینه‌های Google یا Facebook را انتخاب کنید و توسط حساب کاربری ای که قبلا ساختید وارد شوید.

حال بررسی کنید که امکان ویرایش اطلاعات را دارید یا خیر.

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

دیتابیس SQL Azure را بررسی کنید

در Server Explorer دیتابیس ContactDB را پیدا کنید. روی آن کلیک راست کرده و Open in SQL Server Object Explorer را انتخاب کنید.

توجه: اگر نمی‌توانید گره SQL Databases را باز کنید و یا ContactDB را در ویژوال استودیو نمی‌بینید، باید مراحلی را طی کنید تا یک پورت یا یکسری پورت را به فایروال خود اضافه کنید. دقت داشته باشید که در صورت اضافه کردن Port Range‌ها ممکن است چند دقیقه زمان نیاز باشد تا بتوانید به دیتابیس دسترسی پیدا کنید.

روی جدول AspNetUsers کلیک راست کرده و View Data را انتخاب کنید.

حالا روی AspNetUserRoles کلیک راست کنید و View Data را انتخاب کنید.

اگر شناسه کاربران (User ID) را بررسی کنید، مشاهده می‌کنید که تنها دو کاربر user1 و اکانت گوگل شما به نقش canEdit تعلق دارند.

Cannot open server login error

اگر خطایی مبنی بر "Cannot open server" دریافت می‌کنید، مراحل زیر را دنبال کنید.

شما باید آدرس IP خود را به لیست آدرس‌های مجاز (Allowed IPs) اضافه کنید. در پرتال مدیریتی Windows Azure در قسمت چپ صفحه، گزینه SQL Databases را انتخاب کنید.

دیتابیس مورد نظر را انتخاب کنید. حالا روی لینک Set up Windows Azure firewall rules for this IP address کلیک کنید.

هنگامی که با پیغام "?The current IP address xxx.xxx.xxx.xxx is not included in existing firewall rules. Do you want to update the firewall rules" مواجه شدید Yes را کلیک کنید. افزودن یک آدرس IP بدین روش معمولا کافی نیست و در فایروال‌های سازمانی و بزرگ باید Range بیشتری را تعریف کنید.

مرحله بعد اضافه کردن محدوده آدرس‌های مجاز است.

مجددا در پرتال مدیریتی Windows Azure روی SQL Databases کلیک کنید. سروری که دیتابیس شما را میزبانی می‌کند انتخاب کنید.

در بالای صفحه لینک Configure را کلیک کنید. حالا نام rule جدید، آدرس شروع و پایان را وارد کنید.

در پایین صفحه Save را کلیک کنید.

در آخر می‌توانید توسط SSOX به دیتابیس خود متصل شوید. از منوی View گزینه SQL Server Object Explorer را انتخاب کنید. روی SQL Server کلیک راست کرده و Add SQL Server را انتخاب کنید.

در دیالوگ Connect to Server متد احراز هویت را به SQL Server Authentication تغییر دهید. این کار نام سرور و اطلاعات ورود پرتال Windows Azure را به شما می‌دهد.

در مرورگر خود به پرتال مدیریتی بروید و SQL Databases را انتخاب کنید. دیتابیس ContactDB را انتخاب کرده و روی View SQL Database connection strings کلیک کنید. در صفحه Connection Strings مقادیر Server و User ID را کپی کنید. حالا مقادیر را در دیالوگ مذکور در ویژوال استودیو بچسبانید. مقدار فیلد User ID در قسمت Login وارد می‌شود. در آخر هم کلمه عبوری که هنگام ساختن دیتابیس تنظیم کردید را وارد کنید.

حالا می‌توانید با مراحلی که پیشتر توضیح داده شد به دیتابیس Contact DB مراجعه کنید.

افزودن کاربران به نقش canEdit با ویرایش جداول دیتابیس

پیشتر در این مقاله، برای اضافه کردن کاربران به نقش canEdit از یک قطعه کد استفاده کردیم. یک راه دیگر تغییر جداول دیتابیس بصورت مستقیم است. مراحلی که در زیر آمده اند اضافه کردن کاربران به یک نقش را نشان می‌دهند.
در SQL Server Object Explorer روی جدول AspNetUserRoles کلیک راست کنید و View Data را انتخاب کنید.

حالا  RoleId را کپی کنید و در ردیف جدید بچسبانید.

شناسه کاربر مورد نظر را از جدول AspNetUsers پیدا کنید و مقدار آن را در ردیف جدید کپی کنید. همین! کاربر جدید شما به نقش canEdit اضافه شد.

نکاتی درباره ثبت نام محلی (Local Registration)

ثبت نام فعلی ما از بازنشانی کلمه‌های عبور (password reset) پشتیبانی نمی‌کند. همچنین اطمینان حاصل نمی‌شود که کاربران سایت انسان هستند (مثلا با استفاده از یک CAPTCHA). پس از آنکه کاربران توسط تامین کنندگان خارجی (مانند گوگل) احراز هویت شدند، می‌توانند در سایت ثبت نام کنند. اگر می‌خواهید ثبت نام محلی را برای اپلیکیشن خود غیرفعال کنید این مراحل را دنبال کنید:
  • در کنترلر Account متدهای Register را ویرایش کنید و خاصیت AllowAnonymous را از آنها حذف کنید (هر دو متد GET و POST). این کار ثبت نام کاربران ناشناس و بدافزارها (bots) را غیر ممکن می‌کند.
  • در پوشه Views/Shared فایل LoginPartial.cshtml_ را باز کنید و لینک Register را از آن حذف کنید.
  • در فایل  Views/Account/Login.cshtml نیز لینک Register را حذف کنید.
  • اپلیکیشن را دوباره منتشر کنید.


قدم‌های بعدی

برای اطلاعات بیشتر درباره نحوه استفاده از Facebook بعنوان یک تامین کننده احراز هویت، و اضافه کردن اطلاعات پروفایل به قسمت ثبت نام کاربران به لینک زیر مراجعه کنید.
برای یادگیری بیشتر درباره ASP.NET MVC 5 هم به سری مقالات Getting Started with ASP.NET MVC 5 می توانید مراجعه کنید. همچنین سری مقالات Getting Started with EF and MVC  مطالب خوبی درباره مفاهیم پیشرفته EF ارائه می‌کند.
مطالب
پروسیجرها و شنود پارامترها در SQL Server
در اس کیو ال سرور 2016، قابلیت غیر فعال نمودن parameter sniffing در سطح بانک اطلاعاتی مهیا شده است. اما چرا؟


قبل از پاسخگویی به سؤال بالا، به یک سری مقدمات نیاز است:

وقتی یک کوئری به اس کیو ال ارسال می‌شود، چه اتفاقی رخ می‌دهد؟
وقتی یک کوئری ارسال می‌شود، تعدادی از پروسس‌ها بر روی کوئری شروع به فعالیت‌هایی مانند مهیا نمودن داده‌های بازگشتی، یا ذخیره سازی و ... می‌کنند.
 پروسس‌ها به دو دسته زیر تقسیم می‌شوند:
  1. پروسس‌هایی که در relational engine رخ می‌دهند
  2. پروسس‌هایی که در storage engine رخ می‌دهند

در relational engine، هر کوئری pars شده و سپس بوسیله query optimizer پردازش و پلن اجرایی (execution plan) آن که بفرمت باینری است، ایجاد می‌شود و به storage engine ارسال می‌گردد. در storage engine پروسس‌هایی مانند قفل گذاری، نگهداری ایندکس‌ها و تراکنش‌ها رخ می‌دهد. هنگامیکه اس کیو ال سرور کوئری را دریافت می‌نمایند، آن را بلافاصله به relational engine ارسال می‌کند. سپس نحو (syntax) آن بررسی می‌شود؛ این عمل  query parsing نامیده می‌شود. خروجی عملیات پارسر، یک ساختار درختی (query tree) است. این ساختار درختی مشخص کننده مراحل لازم جهت اجرای کوئری ارائه شده می‌باشد.
اگر یک کوئری شامل DML نباشد (مانند ساخت جدول)، علمیات بهبود برروی آن صورت نخواهد گرفت. ولی در صورتیکه کوئری ارسالی، DML باشد، درخت اشاره شده در بالا به algebrizer فرستاده می‌شود که وظیفه آن تفسیر و بررسی کلیه نام اشیاء، جداول و ستون‌های اشاره شده در متن کوئری است. فرآیند algebrizer بسیار مهم و حیاتی است؛ بدلیل اینکه در کوئری ممکن است اشاره کننده‌هایی به اشیایی باشند که در بانک اطلاعاتی موجود نیست. خروجی algebrizer یک query processor tree باینری است که به بهبود دهنده کوئری ارسال می‌گردد. 

معرفی Query Optimizer (بهبود دهنده پرس و جو)

بهبود دهنده، بهترین مسیر اجرای کوئری را مشخص می‌کند. این بهبود دهنده است که مشخص می‌کند که اطلاعات بوسیله ایندکس دریافت شوند، یا اینکه از چه اتصالی استفاده شود و الی آخر. این تصمیمات براساس محاسبات هزینه‌های (میزان پردازش لازم cpu و I/O) پلن اجرایی صورت خواهد پذیرفت. بهمین دلیل به پلن cost-based نیز شناخته می‌شود.
هنگامیکه کوئری ساده‌ای مانند دریافت اطلاعات از یک جدول، که بر روی آن ایندکس گذاری انجام نشده‌است، ارسال شود، بهبود دهنده بجای مشخص نمودن یک پلن مناسب بهینه، از یک پلن ساده (trivial) استفاده می‌کند. ولی برعکس در صورتیکه کوئری trivial نباشد (یعنی مثلا کوئری به گونه‌ای باشد که از ایندکس‌ها به شکل صحیحی استفاده شده باشند)، بهبود دهنده یک پلن مناسب را براساس اطلاعات آماری مهیا شده در اس کیو ال سرور، تولید و انتخاب می‌نماید.

اطلاعات آماری از ستون‌ها و ایندکس‌ها جمع آوری می‌شود. این اطلاعات شامل نحوه توزیع داده، یکتایی و انتخاب شوندگی است. این اطلاعات توسط یک histogram ارائه می‌شود. اگر اطلاعات آماری برای یک ستون و یا ایندکس وجود داشته باشد، بهبود دهنده از آن‌ها برای محاسبات خود استفاده خواهد کرد. اطلاعات آماری بصورت خودکار برای تمام ایندکس‌ها و یا هر ستونی که بشود بر روی آن‌ها where یا join نوشت، فراهم خواهد شد.
بهبود دهنده با مقایسه پلن‌ها براساس بررسی تفاوت‌های انواع joinها، چیدمان مجدد ترتیب join و بررسی ایندکس‌های مختلف و سایر فعالیت‌های دیگر، پلن مناسب را انتخاب و از آن استفاده می‌کند. در طی هر کدام از فعالیت‌های اشاره شده، زمان اجرای آن‌ها نیز تخمین زده (estimated cost) خواهد شد و در پایان، زمان کل تخمینی بدست خواهد آمد و بهبود دهنده از این زمان برای انتخاب پلن مناسب بهره خواهد برد. باید توجه داشت که این زمان تقریبی است. زمانیکه بهبود دهنده پلن اجرایی انتخاب می‌کند، یک actual plan را ایجاد و در حافظه ذخیره می‌شود؛ بنام plan cache. البته درصورتیکه پلن مشابه و بهینه‌تری وجود نداشته باشد. 

استفاده مجدد از پلن ها

تولید پلن هزینه بر است. به‌همین دلیل اس کیوال سرور اقدام به ذخیره سازی و نگهداری آن‌ها می‌کند تا بتواند از آن‌ها مجددا استفاده نماید؛ البته تا جایی که مقدور باشد. هنگامیکه آن‌ها تولید می‌شوند، در قسمتی از حافظه بنام plan cache ذخیره می‌شوند. به این عمل procedure cache نیز گفته می‌شود.

هنگامیکه کوئری به سرور ارسال می‌شود، بوسیله بهبود دهنده، یک estimated plan ایجاد خواهد شد و قبل از اینکه به storage engine ارسال شود، بهبود دهنده estimated plan را با actual execution planهای موجود در plan cache مقایسه می‌کند. در صورتیکه یک actual plan را مطابق با estimated plan پیدا نماید، از آن مجدد استفاده خواهد کرد. این استفاده مجدد به عدم تحمیل سربار اضافه‌ای به سرور جهت کوئری‌های بزرگ و پیچیده که در زمان واحد، هزاران بار اجرا خواهند شد، منجر می‌شود.
هر پلن فقط یکبار در حافظه ذخیره خواهد شد. ولی در مواقعی با تشخیص بهبود دهنده و هزینه پلن، یک کوئری می‌تواند پلن دیگری نیز داشته باشد. بنابراین پلن دوم نیز با مجموعه عملیاتی متفاوت، جهت اجرای موازی (parallel execution) برای یک کوئری ایجاد و در حافظه ذخیره می‌شود.
پلن‌های اجرایی برای همیشه در حافظه باقی نخواهند ماند. پلن‌های اجرایی دارای طول عمری طبق فرمول حاصل ضرب هزینه، در تعداد دفعات می‌باشند. مثلاً پلنی با هزینه 10 و تعداد دفعات اجرای 5، طول عمر 50 را خواهد داشت. پروسس lazywriter که یک پروسس داخلی است وظیفه آزاد سازی تمام انواع کش‌ها، از جمله پلن کش را دارد. این پروسس در بازه‌های مشخص، تمام اشیاء درون حافظه را بررسی کرده و یک واحد از طول عمر آن‌ها می‌کاهد.
در موارد زیر، یک پلن از حافظه پاک خواهد شد:
1. به حافظه بیشتری نیاز باشد
2. طول عمر پلن صفر شده باشد 

حال فرض کنید شما یک پروسیجر یا یک کوئری پارامتری دارید (پارامتر ورودی: شناسه سفارش یا نال) که کلیه محصولات سفارش داده شده یا محصولات یک سفارش خاص را نمایش می‌دهد. هنگامی که SQL Server optimizer پلن این کوئری را ایجاد می‌کند و یا آن را کامپایل می‌کند، به پارامترهای ورودی این پروسیجر گوش می‌دهد (نال یا یک شناسه سفارش). optimizer بوسیله column statistics از تعداد رکوردهایی که بازگشت داده می‌شود، برآوردی می‌کند (مثلا 40 رکورد). سپس یک پلن مناسب را انتخاب می‌کند و آن را برای اجرا ارسال می‌کند و پلن را ذخیره می‌نماید.
جمله آخر، معمولا باعث ایجاد مشکل می‌شوند.
اگر optimizer تکست کوئری مشابهی را مشاهده نماید، ولی با پارامترهای متفاوت، به کش پلن مراجعه کرده و اگر در آن جا قرار داشت، از آن مجددا استفاده می‌نماید. این استفاده مجدد خوب است؛ اما  درصورتیکه پارامتر ارسالی نال باشد چه اتفاقی رخ می‌دهد؟ جدول سفارشات محصول بسیار حجیم است و متاسفانه از پلنی که برای بازگشت 40 رکورد قبلا ایجاد شده، برای بازگشت این حجم بالای از رکوردها استفاده می‌شود که این کشنده است.
هیچ تضمینی وجود ندارد که از وقوع این اتفاق جلوگیری نمایید؛ اما می‌توانید در هنگام توسعه، پروسیجر را شناسایی و نسبت به رفع آنها اقدام نمایید. ابتدا کش پلن را خالی نمایید و سپس پروسیجر را با مقادیر متفاوت، اجرا نمایید. در صورتیکه پلن‌های متفاوتی مشاهده نمودید، این یک علامت هشدار است و می‌بایست نسبت به رفع آن‌ها اقدام فوری نمایید. 
مطالب
ارسال فایل در ASP.NET MVC و اعتبار سنجی سمت کاربر
پیشنیازها:
- نحوه ارسال فایل به سرور توسط ASP.NET MVC
- نحوه اعتبار سنجی سمت سرور ارسال فایل‌ها

در ASP.NET MVC برای آپلود فایل‌ها عموما عنوان می‌شود که از تگ input به نحو زیر استفاده شود:
<input type="file" name="file" />
مشکلی که با این روش وجود دارد، عدم فعال شدن اعتبار سنجی سمت کاربر در حد مثلا «لطفا یک فایل را انتخاب کنید» است. برای فعال سازی آن می‌توان از همان روش unobtrusive معرفی شده در ASP.NET MVC 3 به نحو زیر به صورت دستی استفاده کرد:
<input type="file" data-val="true" data-val-required="لطفا فایلی را انتخاب کنید" name="file" />
@Html.ValidationMessage("file")
در اینجا ویژگی‌های data-val و data-val-required به صورت خودکار اعتبار سنجی سمت کاربر را فعال خواهند کرد (شبیه به required field validator عمل می‌کنند).

از این نکته خصوصا در طراحی html helperهای سفارشی نیز می‌توان استفاده کرد.
برای مثال فرض کنید مشغول طراحی یک کنترل captcha هستید. قسمتی از آن مربوط به دریافت ورودی از کاربر است. به علاوه، پیغام خطایی هم که باید نمایش داده شود نیز باید توسط کاربر قابلیت سفارشی شدن را داشته باشد (و نمی‌توان آن‌را توسط یک attribute به سادگی به یک مدل خاص انتساب داد).
در این حالت تنظیم data-val و data-val-required به کمک anonymously typed objects پارامتر htmlAttributes میسر نیست (چون بین حروف آن dash وجود دارد) و باید از overload و نوع dictionary دار آن به نحو زیر استفاده کرد:

htmlHelper.TextBox("CaptchaInputText", string.Empty,
                          new Dictionary<string, object>
                          {
                            { "data-val", "true"},
                            { "data-val-required", validationErrorMessage},
                          })

مطالب
بررسی تفاوت بین DTO و POCO
در ابتدا اجازه بدهید تعریف درستی از این دو واژه، ارائه کنیم.

DTO (Data Transfer Object)
به بیان خیلی ساده، DTO‌ها برای انتقال اطلاعات استفاده می‌شوند؛ پس هیچ منطق و رفتاری در این اشیاء تعریف نمی‌شود .اگر در DTO منطقی پیاده سازی شود، دیگر به آن DTO گفته نمی‌شود. اجازه بدید منظورمان را از منطق یا رفتار مشخص کنیم. منطق یا رفتار، همان متدهایی هستند که در نوع داده خود تعریف میکنیم. در #C، یک DTO تنها از خصوصیت‌ها (Properties) که از بلوک‌های Get و Set تشکیل شده‌اند، ساخته می‌شود. البته بدون کدهایی جهت اعتبار سنجی (Validation) مقادیر.

سؤال: وضعیت attribute ‌ها و Metadata‌ها چه می‌شود؟
خیلی غیر معمول نیست که از metadata‌ها در DTO، به‌منظور اعتبار سنجی یا اهداف خاص، استفاده کنیم. بعضی از attribute‌ها هیچ رفتاری را به DTO‌ها اضافه نمی‌کنند؛ ولی استفاده از DTO‌ها را در بخش‌های دیگر سیستم، ساده‌تر می‌کنند. در نتیجه هیچکدام از attribute ‌ها و metadata‌ها، شرایط DTO بودن را نقض نمی‌کنند.

مدل‌های دیگری مثل ViewModels‌ها و API Model‌ها چه می‌شوند؟
واژه DTO خیلی مبهم است. تنها چیزی که بیان می‌کند این است که شیء است و فقط و فقط شامل اطلاعات است و رفتاری ندارد. در این تعریف درباره‌ی کاربرد مورد نظر یک DTO چیزی گفته نشده. در بسیاری از معماری‌ها، DTO نقش خاصی را ایفا می‌کند. بطور مثال در معماری MVC، از DTO‌ها برای انقیاد داده (Binding) و ارسال اطلاعات به یک View استفاده میکنند. به همین خاطر این DTO‌ها بعنوان ViewModel، در معماری MVC شناخته می‌شوند که رفتاری را در خود تعریف نمی‌کنند و تنها فرمت اطلاعات مورد انتظار یک View را مهیا می‌کنند.
پس در این سناریوی خاص، ViewModel نوعی DTO می‌باشد. اما باید دقت داشته باشید، همه ViewModel‌‌ها را نمی‌توان DTO محسوب کرد؛ مثلا در معماری MVVM، ویوو مدل‌های تعریف شده، شامل رفتار هم می‌باشند. حتی در معماری MVC نیز گاهی اوقات منطقی به  ViewModel‌‌ها اضافه می‌شود که دیگر به آنها DTO نمی‌گوییم.

 
در صورت امکان، نام DTO‌ها را بر اساس استفاده‌ی آنها تعیین کنید. بطور مثال کلاسی با نام FoodDTO، مشخص نمی‌کند که این نوع، کجا و چگونه قرار است در معماری برنامه شما مورد استفاده قرار  بگیرید؛ برعکس نامگذاری به صورت FoodViewModels کاربرد آن را صراحتا بیان می‌کند.
مثالی از DTO در زبان سی شارپ :
public class ProductViewModel
{
  public int ProductId { get; set; }
  public string Name { get; set; }
  public string Description { get; set; }
  public string ImageUrl { get; set; }
  public decimal UnitPrice { get; set; }
}

کپسوله سازی و DTO ها 
کپسوله سازی، یکی از اصول برنامه نویسی شیءگرا می‌باشد. اما این کپسوله سازی به DTO‌ها اعمال نمی‌شوند. به این علت که هدف کپسوله سازی، پنهان کردن فرآیند پشت صحنه‌ی ذخیره سازی اطلاعات است؛ اما در DTO هیچ فرآیندی پیاده سازی نشده و نباید هیچ State پنهانی وجود داشته باشد. پس بحث Encapsulation در DTO منتفی است. پس کار را برای خودتان سخت نکنید؛ با تعریف private setter ‌ها یا تبدیل کردن DTO به یک شیء غیرقابل تغییر (immutable). شما باید به‌راحتی بتوانید عملیات ایجاد، نوشتن و خواندن DTO‌‌ها را انجام دهید؛ همچنین باید بتوانید عملیات سریالایز کردن بر روی DTO‌‌ها را بدون فرآیند سفارشی اضافه‌ای، انجام دهید.

Field ها  یا Property ها 
سؤالی که مطرح می‌شود این است که وقتی کپسوله سازی در DTO مفهومی ندارد، چرا باید همیشه از property ‌ها استفاده کنیم؟ چرا از فیلد‌ها استفاده نکنیم (فیلد‌های public )؟
ما می‌توانیم هم از property استفاده کنیم و هم از field‌ها؛ اما بعضی از فریم ورک‌ها که کار Serialization را انجام می‌دهند، فقط با property ‌ها کار می‌کنند. بنابراین بسته به نیاز خودتان، از field‌های عمومی یا property‌ها استفاده کنید. اما عموما از Property استفاده میکنند. البته در این پیوند، پرسش و پاسخ مفصلی در این رابطه وجود دارد.

غیرقابل تغییر بودن (Immutability) و نوع رکورد ( Record Type )
غیرقابل تغییر بودن، یکی از مزیت‌های مهم در توسعه نرم افزار است. اما همانطور که در مثل بالا بیان شد، نیازی به غیرقابل تغییر کردن DTO‌‌ها نیست. با ارائه رکورد در سی شارپ 9  شرایط کمی تغییر کرد. شاید عبارت مخفف دیگری که اضافه شده Data transfer Records یا (DTRs) است. یکی از روش‌های تعریف DTR در سی شارپ 9، به شکل زیر است:
public record ProductDTO(int Id, string Name, string Description);

البته روش دیگری هم وجود دارد که شما property‌ها را تعریف کنید و از طریق سازنده، مقدار دهی شوند. ویژگی جدید init-only این امکان را فراهم می‌کند که فقط در زمان مقدار دهی اولیه (initialization)، خصوصیات مقداردهی شوند و در ادامه‌ی چرخه حیات شیء، property ‌ها فقط خواندنی هستند. این ویژگی، record‌ها را غیر قابل تغییر می‌کند.
مثال:
public record ProductDTO
{
  public int Id { get; init; }
  public string Name { get; init; }
}
var dto = new ProductDTO { Id = 1, Name = "some name" };

کلاس‌های POCO یا همان Plain Old CLR/C# Object
شی Plain Old چیست؟ هر شیءای که Plain Old باشد، می‌تواند در هر جایی از برنامه‌ی ما مورد استفاده قرار بگیرد؛ حتی در کلاس‌های Test برنامه. این اشیاء هیچگونه وابستگی برای اجرا وظایف خود، به بانک‌های اطلاعاتی و کتابخانه‌های ثالت ندارند.
برای درک بهتر این نوع کلاس‌ها، به مثال زیر دقت کنید:
public class Product : DataObject<Product>
{
  public Product(int id)
  {
    Id = id;
    InitializeFromDatabase();
  }
  private void InitializeFromDatabase()
  {
    DataHelpers.LoadFromDatabase(this);
  }
  public int Id { get; private set; }
  // other properties and methods
}
همانطور که مشاهده میکنید، این کلاس به متد استاتیکی برای کار با دیتابیس وابسته است؛ در نتیجه باعث میشود که کل کلاس، به وجود بانک اطلاعاتی وابسته شود. همچنین با ارث بری از کلاس پایه‌ی دیگری، وابستگی به یک کتابخانه‌ی ثالث ایجاد شده‌است. اجرای آزمون واحد برای چنین کلاسی، سبب fail شدن عملیات می‌شود. به این علت که ارتباط با بانک اطلاعاتی مورد نیاز متد DataHelpers، تامین نشده‌است. این شرایط، مثالی از الگوی Active Record Pattern می‌باشند. همچنین این کلاس دسترسی به منبع داده را در درون خود گنجانده است که این به معنای نقض اصل Persistence Ignorant (اصل Persistence Ignorance به طور خلاصه بیان می‌کند که در تحلیل و طراحی Business Logic به موضوع ذخیره‌سازی (Persistence) فکر نکنید (تا جای ممکن) یا به عبارت دیگر، ذهن خود را درگیر پیچیدگی‌های ذخیره سازی نکنید. برگرفته شده از breakpoint.blog.ir : روح الله دلپاک)می باشد. یکی از ویژگی‌های POCO عدم نقض الگوی فوق است.

مثالی از POCO : 
public class Product
{
  public Product(int id)
  {
    Id = id;
  }

  private Product()
  {
    // required for EF
  }

  public int Id { get; private set; }
  // other properties and methods
}
این کلاس یک POCO است:
  • برای اجرای وظایف خود به فریم ورک ثالثی وابسته نیست.
  • به کلاس پایه‌ای ( Base class) نیاز ندارد.
  • وابستگی به متد استاتیکی ندارد.
  • می تواند در هر جایی از پروژه، نمونه سازی شود.
  • اصل Persistence Ignorant را بیشتر رعایت کرده، نه بطور کامل؛ چون یک سازنده دارد که به کتابخانه‌ی ثالثی نیازمند است (سازنده‌ی بدون پارامتر که مورد نیاز EF می‌باشد).

POCO و DTO :

شاید این دو مفهموم گیج کننده باشند، ولی DTO همان POCO هست. اگر یک کلاس، DTO باشد، حتما POCO نیز هست. (مرور ویژگی‌های دو مورد در بخش‌های قبلی) ولی برعکس این وضعیت ممکن است صادق نباشد؛ مثال قبلی که در آن وابستگی به کتابخانه‌ی ثالثی در سازنده‌ی بدون پارامتر وجود داشت، DTO بودن را نقض می‌کرد. پس اگر هر دو حالت صادق بود، میتوان گفت این دو مفهوم یکی است.
مطالب
توسعه یک Web API با استفاده از ASP.NET Core و MongoDb (قسمت اول)

در این مقاله قصد داریم یک Api تحت وب را با استفاده از فریم‌ورک ASP.NET Core توسعه دهیم تا عملیات CRUD را بر روی دیتابیس MongoDb که یکی از محبوب‌ترین دیتابیس‌های NoSql است، انجام دهد. قبل از شروع کار باید ویژوال استودیو نسخه‌ی 2019 را نصب داشته باشید؛ به‌طوریکه ورک لود ASP.NET and web development را  هم حتما همراه آن نصب کرده باشید. علاوه بر آن باید .Net Core SDK 3.0+ و دیتابیس MongoDb را هم نصب کنید که می‌توانید آنها را از آدرس‌های زیر دانلود کنید. نگران نصب MongoDb هم نباشید چون نکته خاصی ندارد. 

https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/

https://dotnet.microsoft.com/download/dotnet-core

نسخه‌ی جاری MongoDb، 4.4 است که پس از نصب، به صورت پیشفرض در آدرس C:\Program Files\MongoDB قرار می‌گیرد. در مسیر C:\Program Files\MongoDB\Server\4.4\bin فایل‌های اصلی MongoDb قرار دارند و برای تعامل با MongoDb، به این فایل‌ها نیاز خواهیم داشت. پس برای اینکه به راحتی بتوان در هر جای سیستم از طریق پاورشل به این فایل‌ها دسترسی داشته باشید، این مسیر را به Path environment variable ویندوز اضافه کنید.

MongoDb دارای یک بخش اصلی یا اصطلاحا یکdaemon  است که وظیفه‌ی آن، پردازش درخواست‌هایی است که برای کار با داده‌ها صادر می‌شود. در حقیقت همه کارهایی که ما با داده‌ها انجام می‌دهیم مثلا دسترسی به داده‌ها و دستکاری آن‌ها، از طریق این daemon انجام می‌گیرد و پشت صحنه‌ی این daemon، با Storage Engine کار خواهد کرد.  برای اجرای daemon، پاور شل را باز کنید و دستور mongod را وارد کنید. اگر با تنظیمات پیشفرض اجرا کنید، بر روی پورت 27017 بالا آمده و فایل‌های دیتابیس را هم در مسیر C:\data\db قرار می‌دهد. اگر این مسیر را نداشته باشید، با خطا مواجه می‌شوید. یا باید این مسیر را تعریف کنید و یا از سوئیچ dbpath -- استفاده کنید تا مسیر فایل دیتابیس را تغییر دهید. پس پاور شل را باز کنید و دستور زیر را وارد کنید.

>> mongod --dbpath C:\BooksData
پس از اجرا شدن daemon ، باید از طریق شل mongo که یک شل جاوااسکریپتی است، به آن متصل شویم. از طریق این شل می‌توان کوئری‌ها و دستورات دستکاری داده‌ها را بر روی دیتابیس MongoDb اجرا کنیم. پس یک کنسول پاورشل جدید را باز کرده و دستور زیر را در آن وارد کنید.
>> mongo
پس از متصل شدن به شل، از طریق دستورuse ، یک دیتابیس را به نام BookstoreDb بسازید. بنابراین دستور زیر را وارد کنید.

>> use BookstoreDb
توجه کنید اگر از قبل این دیتابیس وجود داشته باشد، از آن استفاده می‌کند و گرنه آن را خواهد ساخت. بعد از اینکه پیغام switched to db BookstoreDb را مشاهده کردید، می‌توانید هر جا که به این دیتابیس نیاز داشته باشید، از دستور db استفاده نمایید که اشاره به دیتابیسی دارد که آن را use کرده‌ایم.

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

>> db.createCollection('Books')
با این دستور، یک کالکشن با نام Books ساخته می‌شود. اگر پیغام { ok" : 1" } را دیدید، مطمئن باشید که کالکشن Books به درستی ساخته شده‌است. در واقع بعد از ساختن اولین کالکشن، دیتابیس ساخته می‌شود. با دستور show dbs می‌توانید دیتابیس‌های ساخته شده را ببینید.

بعد از ساختن کالکشن، می‌خواهیم مقداری دیتا را در آن قرار دهیم. معادل رکورد در دیتابیس‌های رابطه‌ای، در دیتابیس مونگو دی‌بی، Document نام دارد که مانند رکوردهای دیتابیس‌های رابطه‌ای نیست و ساختار و اسکیمای خاصی ندارد؛ یعنی می‌توان هر دیتایی را با هر ساختاری در آن ذخیره کرد. می‌خواهیم دو Document را به کالکشن Books اضافه کنیم. پس دستور زیر را وارد می‌کنیم. توجه کنید داده‌ها با ساختار json وارد می‌شوند.

>> db.Books.insertMany([{'Name':'Design Patterns','Price':54.93,'Category':'Computers','Author':'Ralph Johnson'}, {'Name':'Clean Code','Price':43.15,'Category':'Computers','Author':'Robert C. Martin'}])
بعد از اجرای دستور بالا، نتایج زیر را در خروجی می‌بینید که گویای این مطلب است که دو Document جدید اضافه شده‌است و خود سرور، به هر یک از داکیومنت‌ها، یک شناسه یا آی دی اختصاص داده‌است و Ack درست هم برگشت داده شده‌است که به معنای انجام درست کار است. اگر می‌خواهید خودتان به داکیومنت‌هایی که می‌سازید آی دی بدهید، باید از پراپرتی id_ استفاده نمایید.
 {
      "acknowledged" : true,
      "insertedIds" : [
         ObjectId("5bfd996f7b8e48dc15ff215d"),
         ObjectId("5bfd996f7b8e48dc15ff215e")
      ]
}
برای اینکه محتویات کالکشنی را مشاهده کنید، دستور زیر را وارد کنید. همانطور که اشاره شد، db، به دیتابیس ساخته شده اشاره دارد؛ Books همان کالکشن‌مان است و با استفاده از متد find می‌توان داده‌های کالکشن را جستجو کرد که در این مثال ما، هیچ فیلتری به آن نداده‌ایم. پس در نتیجه تمام دیتاها را بر می‌گرداند. متد pretty هم داده‌های برگشتی را در یک ساختار مرتب نمایش می‌دهد.
>> db.Books.find().pretty()
و خروجی زیر را مشاهده خواهید کرد
 {
      "_id" : ObjectId("5bfd996f7b8e48dc15ff215d"),
      "Name" : "Design Patterns",
      "Price" : 54.93,
      "Category" : "Computers",
      "Author" : "Ralph Johnson"
},
{
      "_id" : ObjectId("5bfd996f7b8e48dc15ff215e"),
      "Name" : "Clean Code",
      "Price" : 43.15,
      "Category" : "Computers",
      "Author" : "Robert C. Martin"
}
حالا دیتابیس ما آماده شده‌است. در بخش‌های بعدی با استفاده از فریم ورک   ASP.NET Core Web Api  ، Api هایی خواهیم نوشت که داده‌های این دیتابیس را به بیرون ارائه خواهد داد. 

نظرات مطالب
پیاده سازی JSON Web Token با ASP.NET Web API 2.x
- قسمت web api برنامه با mvc یکپارچه هست؟ اگر بله، برای کار با web api نیازی به این روش ندارید. همینقدر که کاربر برای مثال از طریق روش‌هایی مانند ASP.NET Identity و یا Forms authentication به سایت وارد شده باشد، کوکی حاصل از اعتبارسنجی آن‌ها، به همراه اعمال Ajax ای مختص Web API هم به صورت خودکار ارسال می‌شود و ... کاربر با موفقیت اعتبارسنجی خواهد شد. پروژه‌ی مطلب «اعمال تزریق وابستگی‌ها به مثال رسمی ASP.NET Identity» یک قسمت Web API محافظت شده هم دارد؛ با این view که کاملا عادی به نظر می‌رسد. چون سیستم یکپارچه هست.
- کاربرد JWT بیشتر در برنامه‌های SPA مانند Angular است: «احراز هویت و اعتبارسنجی کاربران در برنامه‌های Angular»
در اینجا کاربر پس از لاگین (از طریق صفحه‌ی لاگینی، کاملا عادی و معمولی که هیچ اطلاعاتی هم از پیش در آن ذخیره نشده)، چیزی را که سمت کلاینت ذخیره می‌کند (برای مثال در local storage مرورگر)، فقط توکن‌های دریافتی پس از اعتبارسنجی موفق است و نه کلمه‌ی عبور و نه هیچ اطلاعات دیگری. سپس در هر درخواست به منابع محافظت شده‌ی سمت سرور، صرفا این توکن‌ها را ارسال می‌کند تا توسط آن، کار اعتبارسنجی خودکار کاربر صورت گیرد و هویت و همچنین سطوح دسترسی آن مشخص شوند.
- مثالی که در این پروژه ارائه شده، نمونه‌ی دیگر «آزمایش Web APIs توسط Postman - قسمت ششم - اعتبارسنجی مبتنی بر JWT» است؛ جهت ارائه‌ی یک سری مفهوم.
نظرات اشتراک‌ها
فراخوانی بیشتر از یک بار "window.onload"
- اگر از MVC استفاده می‌کنید، در فایل layout
...
    اینجا اسکریپت‌های عمومی قرار گیرند قبل از پایان فایل
    @RenderSection("JavaScript", false)
</body>
</html>
به کمک RenderSection کلیه اسکریپت‌های inline فقط در همین مکان فراخوانی آن قرار می‌گیرند. به این ترتیب در Viewها فقط کافی است یک section به نام JavaScript اضافه کرد تا در این مکان درج شود.
- در ASP.NET Web forms هم می‌شود این نظم رو پدید آورد. از ContentPlaceHolder استفاده کنید. یکی در master page تعریف شود دیگری در وب فرم به ارث رسیده از آن برای قرار دادن اسکریپت‌های خاص همان صفحه.
    ....  سایر قسمت‌های فایل مستر پیج
    <asp:ContentPlaceHolder id='PageScriptPlaceHolder' runat='server'>
    </asp:ContentPlaceHolder>
</body>
حالا وب فرم شما فقط کافی است اسکریپت‌های خاص خودش را با ID فوق درج کنه
<asp:Content ID='ScriptIncludes' runat='server'  ContentPlaceHolderID='PageScriptPlaceHolder'>
  اسکریپت‌های صفحه در اینجا
</asp:Content>
نظرات مطالب
ASP.NET Web API - قسمت اول
REST بیشتر برای مواقعی هست که شما عملیات CRUD انجام میدید. در حالی که با Web API می‌تونید علاوه بر CRUD، کارهای بسیار بیشتری انجام بدید. مفاهیم و قابلیت‌های موجود در ASP.NET MVC مانند فیلترها به خوبی در Web API پشتیبانی و به راحتی قابل استفاده هستند. ضمن اینکه با Web API می‌تونید معماری REST رو با تغییر کوچکی در route پیش فرض به دست بیارید و بدین شکل، مهاجرت از REST به Web API بسیار راحت هست. در اوایل معرفی Web API، از پروتوکل OData نیز پشتیبانی اولیه میشد که متاسفانه مایکروسافت در نسخه‌ی RC این پشتیبانی رو حذف کرد. شاید در نسخه‌های بعدی این قابلیت نیز اضافه بشه که به قدرتمندتر شدن Web API کمک می‌کنه.
ضمناً، پشتیبانی مایکروسافت از WCF REST API نیز به اتمام رسیده و پیشنهاد شده که از Web API استفاده کنید.
http://aspnet.codeplex.com/wikipage?title=WCF%20REST