ASP.NET MVC #17
فیلترهای امنیتی ASP.NET MVC
ASP.NET MVC به همراه تعدادی فیلتر امنیتی توکار است که در این قسمت به بررسی آنها خواهیم پرداخت.
بررسی اعتبار درخواست (Request Validation) در ASP.NET MVC
ASP.NET MVC امکان ارسال اطلاعاتی را که دارای تگهای HTML باشند، نمیدهد. این قابلیت به صورت پیش فرض فعال است و جلوی ارسال انواع و اقسام اطلاعاتی که ممکن است سبب بروز حملات XSS Cross site scripting attacks شود را میگیرد. نمونهای از خطای نمایش داده:
A potentially dangerous Request.Form value was detected from the client (Html="<a>").
بنابراین تصمیم گرفته شده صحیح است؛ اما ممکن است در قسمتی از سایت نیاز باشد تا کاربران از یک ویرایشگر متنی پیشرفته استفاده کنند. خروجی این نوع ویرایشگرها هم HTML است. در این حالت میتوان صرفا برای متدی خاص امکانات Request Validation را به کمک ویژگی ValidateInput غیرفعال کرد:
[HttpPost]
[ValidateInput(false)]
public ActionResult CreateBlogPost(BlogPost post)
از ASP.NET MVC 3.0 به بعد راه حل بهتری به کمک ویژگی AllowHtml معرفی شده است. غیرفعال کردن ValidateInput ایی که معرفی شد، بر روی تمام خواص شیء BlogPost اعمال میشود. اما اگر فقط بخواهیم که مثلا خاصیت Text آن از مکانیزم بررسی اعتبار درخواست خارج شود، بهتر است دیگر از ویژگی ValidateInput استفاده نشده و به نحو زیر عمل گردد:
using System;
using System.Web.Mvc;
namespace MvcApplication14.Models
{
public class BlogPost
{
public int Id { set; get; }
public DateTime AddDate { set; get; }
public string Title { set; get; }
[AllowHtml]
public string Text { set; get; }
}
}
در اینجا فقط خاصیت Text مجاز به دریافت محتوای HTML ایی خواهد بود. اما خاصیت Title چنین مجوزی را ندارد. همچنین دیگر نیازی به استفاده از ویژگی ValidateInput غیرفعال شده نیز نخواهد بود.
به علاوه همانطور که در قسمتهای قبل نیز ذکر شد، خروجی Razor به صورت پیش فرض Html encoded است مگر اینکه صریحا آنرا تبدیل به HTML کنیم (مثلا استفاده از متد Html.Raw). به عبارتی خروجی Razor در حالت پیش فرض در مقابل حملات XSS مقاوم است مگر اینکه آگاهانه بخواهیم آنرا غیرفعال کنیم.
مطلب تکمیلی
مقابله با XSS ؛ یکبار برای همیشه!
فیلتر RequireHttps
به کمک ویژگی یا فیلتر RequireHttps، تمام درخواستهای رسیده به یک متد خاص باید از طریق HTTPS انجام شوند و حتی اگر شخصی سعی به استفاده از پروتکل HTTP معمولی کند، به صورت خودکار به HTTPS هدایت خواهد شد:
[RequireHttps]
public ActionResult LogOn()
{
}
فیلتر ValidateAntiForgeryToken
نوع دیگری از حملات که باید در برنامههای وب به آنها دقت داشت به نام CSRF یا Cross site request forgery معروف هستند.
برای مثال فرض کنید کاربری قبل از اینکه بتواند در سایت شما کار خاصی را انجام دهد، نیاز به اعتبار سنجی داشته باشد. پس از لاگین شخص و ایجاد کوکی و سشن معتبر، همین شخص به سایت دیگری مراجعه میکند که در آن مهاجمی بر اساس وضعیت جاری اعتبار سنجی او مثلا لینک حذف کاربری یا افزودن اطلاعات جدیدی را به برنامه ارائه میدهد. چون سشن شخص و کوکی مرتبط به سایت اول هنوز معتبر هستند و شخص سایت را نبسته است، «احتمال» اجرا شدن درخواست مهاجم بالا است (خصوصا اگر از مرورگرهای قدیمی استفاده کند).
بنابراین نیاز است بررسی شود آیا درخواست رسیده واقعا از طریق فرمهای برنامه ما صادر شده است یا اینکه شخصی از طریق سایت دیگری اقدام به جعل درخواستها کرده است.
برای مقابله با این نوع خطاها ابتدا باید داخل فرمهای برنامه از متد Html.AntiForgeryToken استفاده کرد. کار این متد ایجاد یک فیلد مخفی با مقداری منحصربفرد بر اساس اطلاعات سشن جاری کاربر است، به علاوه ارسال یک کوکی خودکار تا بتوان از تطابق اطلاعات اطمینان حاصل کرد:
@using (Html.BeginForm()) {
@Html.AntiForgeryToken()
در مرحله بعد باید فیلتر ValidateAntiForgeryToken جهت بررسی مقدار token دریافتی به متد ثبت اطلاعات اضافه شود:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateBlogPost(BlogPost post)
در اینجا مقدار دریافتی از فیلد مخفی فرم :
<input name="__RequestVerificationToken" type="hidden" value="C0iPfy/3T....=" />
با مقدار موجود در کوکی سایت بررسی و تطابق داده خواهند شد. اگر این مقادیر تطابق نداشته باشند، یک استثنا صادر شده و از پردازش اطلاعات رسیده جلوگیری میشود.
علاوه بر اینها بهتر است حین استفاده از متد و فیلتر یاد شده، از یک salt مجزا نیز به ازای هر فرم، استفاده شود:
[ValidateAntiForgeryToken(Salt="1234")]
@Html.AntiForgeryToken(salt:"1234")
به این ترتیب tokenهای تولید شده در فرمهای مختلف سایت یکسان نخواهند بود.
به علاوه باید دقت داشت که ValidateAntiForgeryToken فقط با فعال بودن کوکیها در مرورگر کاربر کار میکند و اگر کاربری پذیرش کوکیها را غیرفعال کرده باشد، قادر به ارسال اطلاعاتی به برنامه نخواهد بود. همچنین این فیلتر تنها در حالت HttpPost قابل استفاده است. این مورد هم در قسمتهای قبل تاکید گردید که برای مثال بهتر است بجای داشتن لینک delete در برنامه که با HttpGet ساده کار میکند، آنرا تبدیل به HttpPost نمود تا میزان امنیت برنامه بهبود یابد. از HttpGet فقط برای گزارشگیری و خواندن اطلاعات از برنامه استفاده کنید و نه ثبت اطلاعات.
بنابراین استفاده از AntiForgeryToken را به چک لیست اجباری تولید تمام فرمهای برنامه اضافه نمائید.
مطلب مشابه
Anti CSRF module for ASP.NET
فیلتر سفارشی بررسی Referrer
یکی دیگر از روشهای مقابله با CSRF، بررسی اطلاعات هدر درخواست ارسالی است. اگر اطلاعات Referrer آن با دومین جاری تطابق نداشت، به معنای مشکل دار بودن درخواست رسیده است. فیلتر سفارشی زیر میتواند نمونهای باشد جهت نمایش نحوه بررسی UrlReferrer درخواست رسیده:
using System.Web.Mvc;
namespace MvcApplication14.CustomFilter
{
public class CheckReferrerAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext != null)
{
if (filterContext.HttpContext.Request.UrlReferrer == null)
throw new System.Web.HttpException("Invalid submission");
if (filterContext.HttpContext.Request.UrlReferrer.Host != "mysite.com")
throw new System.Web.HttpException("This form wasn't submitted from this site!");
}
base.OnAuthorization(filterContext);
}
}
}
و برای استفاده از آن:
[HttpPost]
[CheckReferrer]
[ValidateAntiForgeryToken]
public ActionResult DeleteTask(int id)
نکتهای امنیتی در مورد آپلود فایلها در ASP.NET
هر جایی که کاربر بتواند فایلی را به سرور شما آپلود کند، مشکلات امنیتی هم از همانجا شروع خواهند شد. مثلا در متد Upload قسمت 11 این سری، منعی در آپلود انواع فایلها نیست و کاربر میتواند انواع و اقسام شلها را جهت تحت کنترل گرفتن سایت و سرور آپلود و اجرا کند. راه حل چیست؟
از همان روش امنیتی مورد استفاده توسط تیم ASP.NET MVC استفاده میکنیم. فایل web.config قرار گرفته در پوشه Views را باز کنید (نه فایل وب کانفیگ ریشه اصلی سایترا). چنین تنظیمی را میتوان مشاهده کرد:
برای IIS6 :
<system.web>
<httpHandlers>
<add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>
</system.web>
<system.webServer>
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
</system.webServer>
تنظیم فوق، موتور اجرایی ASP.NET را در این پوشه خاص از کار میاندازد. به عبارتی اگر شخصی مسیر یک فایل aspx یا cshtml یا هر فایل قرار گرفته در پوشه Views را مستقیما در مرورگر خود وارد کند، با پیغام HttpNotFound مواجه خواهد شد.
این روش هم با ASP.NET Web forms سازگار است و هم با ASP.NET MVC؛ چون مرتبط است به موتور اجرایی ASP.NET که هر دوی این فریم ورکها برفراز آن معنا پیدا میکنند.
بنابراین در پوشه فایلهای آپلودی به سرور خود یک web.config را با محتوای فوق ایجاد کنید (و فقط باید مواظب باشید که این فایل حین آپلود فایلهای جدید، overwrite نشود. مهم!). به این ترتیب این مسیر دیگر از طریق مرورگر قابل دسترسی نخواهد بود (با هر محتوایی). سپس برای ارائه فایلهای آپلودی به کاربران از روش زیر استفاده کنید:
public ActionResult Download()
{
return File(Server.MapPath("~/Myfiles/test.txt"), "text/plain");
}
مزیت مهم روش ذکر شده این است که کاربران مجاز به آپلود هر نوع فایلی خواهند بود و نیازی نیست لیست سیاه تهیه کنید که مثلا فایلهایی با پسوندهای خاص آپلود نشوند (که در این بین ممکن است لیست سیاه شما کامل نباشد ...).
علاوه بر تمام فیلترهای امنیتی که تاکنون بررسی شدند، فیلتر دیگری نیز به نام Authorize وجود دارد که در قسمتهای بعدی بررسی خواهد شد.
فصل | عنوان | فایل مرتبط |
11 | آشنایی با MVVM Light Toolkit | + |
12 | اعمال قالبهای متفاوت به برنامههای Silverlight | + |
13 | استفاده از Web Services در Silverlight | + |
14 | آشنایی با مفاهیم مرتبط با شیء Application و مدیریت آن | + |
15 | امنیت در Silverlight | + |
16 | روشهای تعیین اعتبار ورودی کاربر در Silverlight | + |
17 | استفاده از تصاویر و فایلهای چند رسانهای در Silverlight | + |
18 | بررسی جامع تواناییهای کنترل DataGrid | + |
19 | معرفی سایر امکانات و ویژگیهای اختصاصی Silverlight 4 | + |
20 | بررسی اجرای خارج از مرورگر برنامههای Silverlight | + |
Pipe یک متغیر یا عبارت را به عنوان ورودی دریافت کرده و آنرا به شکل دیگری برای نمایش تغییر میدهد. Pipeها معمولا در صفحات HTML مورد استفاده قرار میگیرند. با استفاده از عملگر Pipe (|) به شکل زیر میتوانید Pipe مورد نظر خود را اعمال کنید.
import { Component } from '@angular/core'; @Component({ selector: 'hero-birthday', template: `<p>The hero's birthday is {{ birthday | date }}</p>` }) export class HeroBirthdayComponent { birthday = new Date(1988, 3, 15); // April 15, 1988 }
در این مثال Pipe از قبل ساخته شده date را بر روی متغییر birthday اعمال میکنیم. خروجی کار به شکل زیر خواهد بود.
The hero's birthday is April 15, 1988
لازم به ذکر است Pipe هیچگونه اثری بر روی متغییر birthday نداشته و فقط نحوه نمایش آن را تغییر میدهد.
Pipeهای از پیش ساخته شده
در انگیولار ۲ یکسری Pipe از پیش ساخته شده مانند DatePipe، UpperCasePipe، LowerCasePipe، CurrencyPipe و PercentPipe وجود دارند که شما در تمامی صفحات HTML میتوانید بدون هیچ تنظیم اضافهای از آنها استفاده کنید. لیست Pipeهای از پیش ساخته شده را اینجا مشاهده کنید.
ارسال پارامتر به Pipe
<p>The hero's birthday is {{ birthday | date:"MM/dd/yy" }} </p>
مقدار پارامتر، هر نوع مجازی از Template expressions میتواند باشد (از جمله عبارت رشتهای یا حتی یک خصوصیت از کامپوننت). بنابراین در سرتاسر برنامه و در هر زمان میتوانید با تغییر پارامتر در لحظه، خروجی مدنظر خود را به کاربرنهایی نمایش دهید.
Template expressions: عباراتی (expressions) که بعد از اجرا توسط انگیولار، تبدیل به یک مقدار جهت نمایش در HTML، کامپوننت یا دایرکتیو میشود.
استفاده زنجیرهای از Pipeها
برای اعمال چند Pipe بر روی یک عبارت، میتوان از Pipeها به صورت پشت سر هم استفاده کرد. برای مثال در ادامه میخواهیم علاوه بر اعمال DatePipe با پارامتر fullDate جهت نمایش تاریخ به صورت Friday, April 15, 1988، حروف را نیز به صورت UpperCase نمایش دهیم. لازم به ذکر است برای نمایش حروف به صورت UpperCase از Pipe به همین نام استفاده میکنیم.
<p>The hero's birthday is {{ birthday | date:"fullDate" | uppercase}} </p>
خروجی کار به شکل زیر خواهد بود.
The hero's birthday is FRIDAY, APRIL 15, 1988
استفاده از Pipe در TypeScript
برای کسانیکه با انگیولار یک آشنایی دارند، Pipe در انگولار ۲ معادل filter در انگولار یک است. در انگیولار یک با تزریق سرویس filter$ به کنترلرها یا سرویسها، میتوانستیم filterهای تعریف شده شخصی و از پیش ساخته شده را جهت اعمال بر روی متغیرهای خود، استفاده کنیم. در انگیولار دو نیز این امکان فراهم شدهاست؛ ولی به سادگی تزریق filter$ نیست. یعنی لازم است علاوه بر تزریق Pipe به سرویس یا کامپوننتهای خود، Pipe مورد نظر خود را در لیست providers ماژول خود نیز اضافه کنید. برای نمونه اگر بخواهیم DatePipe را در component خود (نه در template) مورد استفاده قرار دهیم به شکل زیر عمل میکنیم:
import { Component } from '@angular/core'; // اضافه کردن DatePipe از @angular/common import { DatePipe } from '@angular/common'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'app works!'; birthDay = new Date(1988, 3, 15); strBirthDay = ""; // تزریق DatePipe constructor(private datePipe: DatePipe) { this.strBirthDay = this.datePipe.transform(this.birthDay, 'yyyy-MM-dd'); } }
پس از وارد کردن DatePipe از angular/common@ به کامپوننت و تزریق آن از طریق سازنده کامپوننت، برای اعمال Pipe بر روی عبارت مورد نظر خود، از متد transform استفاده میکنیم.
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; import { DatePipe } from '@angular/common' import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, FormsModule, HttpModule ], // افزودن Pipe به Providers providers: [DatePipe], bootstrap: [AppComponent] }) export class AppModule { }
نکتهای در مورد DatePipe و CurrencyPipe
Pipeهای Date و Currency به دلیل استفاده از شی Intl در داخل خود نیاز به ECMAScript Internationalization API دارند. مرورگرهای قدیمی و همچنین مرورگر Safari به دلیل عدم پشتیبانی از این قضیه به هنگام استفاده از این Pipeها دچار مشکل میشوند. برای حل این مشکل کافی است اسکریپت زیر را به صفحه خود اضافه کنید.
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en"></script>
در بخشهای بعدی نحوه ساخت Pipeهای سفارشی و همچنین نکات تکمیلی در مورد Pipeها را بررسی خواهیم کرد.
آشنایی با Flex Layout Box Model
برای طراحی ظاهر یک برنامهی وب نیاز است عناصر آنرا در مکانهای مختلفی از صفحه قرار داد که به آن Layout گفته میشود. برای این منظور عموما 4 روش ذیل مرسوم هستند:
1. Table
2. Float, position, clear
3. CSS Grids
4. FlexBox CSS
امروزه دیگر آنچنان روشهای 1 و 2 به صورت مستقیم مورد استفاده قرار نمیگیرند. CSS Grid روش نهایی طراحی Layout در آینده خواهد بود و در حال حاضر تعداد مرورگرهایی که از آن پشتیبانی میکنند، قابل توجه نیست؛ اما از FlexBox در IE 11، کروم 21 و فایرفاکس 22 به بعد پشتیبانی میشود.
FlexBox CSS، سیلان المانهای قرار گرفتهی در داخل آنرا سبب میشود. در اینجا یک container اصلی وجود دارد که در برگیرندهی المانها است. در تصویر فوق دو محور را مشاهده میکنید. محور افقی از چپ به راست ادامه پیدا میکند. محور عمودی نحوهی ارتباط عناصر را مشخص میکند.
اکنون این سؤال مطرح میشود که چه تفاوتی بین یک Grid و FlexBox CSS وجود دارد؟ در یک Grid طراحی دو بعدی سطرها و ستون وجود دارد. اما به FlexBox باید به صورت سیلان یک بعدی سلولها نگاه کرد. برای مثال عناصر قرار گرفتهی درون Container یا به صورت افقی درون آن گسترده شده و قرار میگیرند و یا به صورت عمودی.
نحوهی تفکر و کارکرد با FlexBox چگونه است؟
در اینجا باید به دو مفهوم دقت داشت:
الف) سیلان عناصر درون Container که میتواند افقی و یا عمودی باشد.
ب) اندازهی المانها که میتواند ثابت و یا نسبی باشد.
یک Container، جهت سیلان عناصر درون آنرا مشخص میکند. المانهای آن، اندازه، فاصلهی از یکدیگر و ترتیب قرارگیری را ارائه میدهند. یک flex container میتواند شامل چندین flex container تو در تو نیز باشد.
نحوهی سیلان عناصر در FlexBox چگونه است؟
برای نمونه طرحبندی متداول ذیل را درنظر بگیرید:
نحوه تفکر در مورد طراحی این طرحبندی، باید از بیرون به درون و از بالا به پایین (سیلان عمودی) باشد:
سپس نحوهی سیلان عناصر درون Containerهای تعریف شده را مشخص میکنیم. برای مثال اولین Container دارای سیلان افقی از چپ به راست خواهد بود که عنصر سوم آن به دلیل اندازههای مشخص دو عنصر قبلی، به سطر دوم منتقل شدهاست.
در ادامه به قسمت میانی میرسیم که آن نیز دارای سیلان افقی از چپ به راست است:
در اینجا نیز میتوان سه Container را متصور شد که وسطی دارای سیلان افقی از راست به چپ است و مواردی که بر اساس اندازهی آنها در یک سطر جا نشدهاند، به سطر بعدی منتقل خواهند شد:
و تمام این سیلانها و انتقال به سطرهای بعدی بر اساس اندازهی المانها صورت میگیرد:
البته در این تصویر یک ایراد هم وجود دارد. با توجه به اینکه در ناحیهی میانی سه Container تعریف خواهند شد. Container ایی که در میان آن قرار میگیرد، دارای سیلان خاص خودش است و اندازههای آن باید نسبت به این Container تعریف شوند و نه نسبت به کل ناحیهی میانی. یعنی بجای اینکه 50 درصد، 25، 25 و 50 درصد را داشته باشیم، اینها در اصل 100 درصد، 50 و 50 درصد و سپس 100 درصد هستند.
معرفی کتابخانهی Angular Flex Layout
برای کار با Flex CSS نیاز است:
- مقدار زیادی کد CSS نوشت.
- نیاز به درک عمیقی از Flex Box دارد.
- نیاز است با باگهای مرورگرها و تفاوتهای پیاده سازیهای آنها در مورد FlexBox آشنا بود.
- نیاز به Prefixing دارد.
- برای Angular طراحی نشدهاست.
جهت رفع این مشکلات و محدودیتها، تیم Angular کتابخانهای را به نام Angular Flex Layout مخصوص نگارشهای جدید Angular طراحی کردهاست. این کتابخانه مستقل از Angular Material است اما عموما به همراه آن استفاده میشود.
مزایای کار با کتابخانهی Angular flex layout
- پیاده سازی TypeScript ایی دارد. در اصل یک سری directives مخصوص Angular است که با TypeScript نوشته شدهاست.
- به صورت پویا و inline تمام CSSهای مورد نیاز را تولید و تزریق میکند.
- به همراه یک API استاتیک است و همچنین یک API واکنشگرا
- با Angular CLI نیز یکپارچه شدهاست.
نصب و تنظیم کتابخانهی Angular Flex layout
برای نصب این کتابخانه، در ریشهی پروژه دستور زیر را صادر کنید:
npm install @angular/flex-layout --save
import { FlexLayoutModule } from "@angular/flex-layout"; @NgModule({ imports: [ FlexLayoutModule ], exports: [ FlexLayoutModule ] }) export class SharedModule { }
کار با API استاتیک Angular Flex layout
API استاتیک Angular Flex layout شامل این مزایا و مشخصات است:
- به صورت یکسری دایرکتیو Angular طراحی شدهاست که به HTML قالب کامپوننتها اضافه میشود.
- از data binding پشتیبانی میکند.
- CSS نهایی را به صورت پویا و inline تولید و به صفحه تزریق میکند. Inline CSS تزریق شده به ویژگیهای styles هر المان تزریق میشوند و موارد مشابه را در صورت وجود بازنویسی میکنند.
- از تشخیص تغییرات پشتیبانی میکند.
- به همراه ویژگیهای fxHide و fxShow است.
- کارآیی مطلوبی دارد.
در اینجا برای تعریف container اصلی از دایرکتیوهای زیر استفاده میشود:
- fxLayout جهتهای flex را مشخص میکند.
<div fxLayout="row" fxLayout.xs="column"></div>
- fxLayoutWrap مشخص میکند که آیا المانها باید به سطر و یا ستون بعدی منتقل شوند یا خیر؟
<div fxLayoutWrap></div>
<div fxLayoutGap="10px"></div>
<div fxLayoutAlign="start stretch"></div>
چند مثال:
و یا حالت راست به چپ آن به صورت زیر است:
و برای تعریف آیتمهای قرار گرفتهی درون containers میتوان از دایرکتیوهای زیر استفاده کرد:
- fxflex برای تعیین اندازه و flex المانها
<div fxFlex="1 2 calc(15em + 20px)"></div>
fxFlex="grow shrink basis"
fxFlex="basis"
- shrink به این معنا است که اگر به اندازهی کافی فضا وجود نداشت، این المان نسبت به سایر المانهای دیگر تا چه اندازهای میتواند کوچک شود.
- basis به معنای اندازهی پیشفرض المان است.
در اینجا اندازهها برحسب پیکسل، درصد و یا calcs, em, cw, vh میتوانند تعیین شوند. همچنین یک سری نام مستعار مانند grow, initial, auto, none, nogrow, noshrink هم قابل استفاده هستند.
- fxflexorder برای تعیین ترتیب قرارگیری یک المان
<div fxFlexOrder="2"></div>
<div fxFlexOffset="20px"></div>
<div fxFlexAlign="center"></div>
<div fxFlexFill></div>
چند مثال:
در اینجا سه نمایشی را که در ذیل تعریف divها مشاهده میکنید بر اساس تغییر اندازهی صفحه حاصل شدهاند. چون آیتم دوم دارای مقدار grow مساوی 5 است، به همین جهت با تغییر اندازهی صفحه و دسترسی به مقدار فضای بیشتر، بزرگتر شدهاست.
یک مثال کامل
اگر علاقمند باشید تا توانمندیهای angular flex layout را در قالب یک مثال کامل مشاهده کنید، به آدرس زیر مراجعه نمائید:
https://tburleson-layouts-demos.firebaseapp.com/#/docs
در این مثال با تغییر گزینههای مختلف، کد معادل angular flex layout آن نیز تولید میشود.
همچنین wiki خود پروژه نیز به همراه مثالهای بیشتری است:
https://github.com/angular/flex-layout/wiki
کار با API واکنشگرای Angular Flex layout
در طراحی واکنشگرا، container و عناصر داخل آن بر اساس تغییرات اندازهی صفحه و یا اندازهی وسیلهی نمایشی، تغییر اندازه و همچنین موقعیت میدهند و این تغییرات بر اساس انطباق با viewport وسیلهی نمایشی صورت میگیرند. به همین جهت برای طراحی واکنشگرا نیاز به Flex CSS و همچنین Media Query است. نوشتن Medial Query و ترکیب آن با Flex CSS کار مشکلی است. به همین جهت Angular Flex layout به همراه یک API واکنشگرا نیز هست که در پشت صحنه Flex CSS را بر اساس طراحی متریال و Medial Queries مورد استفاده قرار میدهد.
اگر علاقمند هستید تا اندازههای واکنشگرای استاندارد متریال را ملاحظه کنید، میتوانید به آدرس زیر مراجعه نمائید (قسمت Breakpoint system آن):
https://material.io/design/layout/responsive-layout-grid.html#breakpoints
برای مثال هر اندازهای کمتر از 600px در گروه extra small قرار میگیرد (با مخفف xs). از 600px تا 1024px در بازهی small (با مخفف sm)، از 1024px تا 1440px در بازهی medium (با مخفف md) و از 1440px تا 1920px در بازهی large (با مخفف lg) و بیشتر از آن در بازهی xlrage قرار میگیرند (با مخفف xl). این اعداد و بازهها، پایهی طراحی API واکنشگرای Angular Flex layout هستند. به همین جهت نام این بازهها در این API به صورت مخفف xs, sm, md, lg, xl درنظر گرفته شدهاند و مورد استفاده قرار میگیرند. همچنین اگر اندازههای مدنظر از این بازهها کمتر باشند، میتوان از lt-sm, lt-md, lt-lg, lt-xl نیز استفاده کرد. در اینجا lt به معنای less than است و یا اگر بازههای مورد نیاز بیش از این اندازهها باشند میتوان با gt-xs, gt-sm, gt-md, gt-lg کار کرد. در اینجا gt به معنای greater than است.
به این مخففها «media query alias» هم گفته میشود و اکنون که لیست آنها مشخص است، تنها کافی است آنها را به API استاتیکی که پیشتر بررسی کردیم، اضافه کنیم. برای مثال:
fxLayout.sm = "..." fxLayoutAlign.md = "..." fxHide.gt-sm = "..."
معادل طراحی آن با API استاتیک Angular Flex Layout به صورت زیر است:
اکنون که این طرحبندی دسکتاپ را داریم، چگونه باید آنرا تبدیل به طرحبندی موبایل، مانند شکل زیر کنیم؟
برای اینکار ابتدا fxLayout.xs را به سطر میانی اضافه میکنیم تا هرگاه به این اندازه رسیدیم، بجای ردیف، تبدیل به ستون شود. سپس توسط fxFlexOrder.xs، در اندازهی xs، محل قرارگیری المانهای این ستون را هم مشخص میکنیم:
همانطور که ملاحظه میکنید کار کردن با این API بسیار سادهاست و نیازی به کارکردن مستقیم با Media Queries و یا برنامه نویسی مستقیم ندارد و تمام آن در قالب HTML یک کامپوننت قابل پیاده سازی است.
یک نکته: مثال کاملی که پیشتر در این بحث مطرح شد، به همراه مثال واکنشگرا نیز هست که برای مشاهدهی اثر آنها بهتر است اندازهی مرورگر را کوچک و بزرگ کنید.
مخفی کردن و یا نمایش قسمتی از صفحه بر اساس اندازهی آن
علاوه بر media query alias هایی که عنوان شد، امکان نمایش و یا مخفی سازی قسمتهای مختلف صفحه بر اساس اندازهی صفحهی نمایشی نیز هست:
<div fxShow fxHide.xs="false" fxHide.lg="true"></div>
تغییر اندازهی قسمتی از صفحه بر اساس اندازهی آن
در مثال زیر اگر اندازهی صفحه gt-sm باشد (بیشتر از small)، اندازهی این div به 100 درصد بجای 50 درصد حالتهای دیگر، تنظیم میشود:
<div fxFlex="50%" fxFlex.gt-sm="100%"></div>
حالتهای ویژهی طراحی واکنشگرا در Angular Flex Layout
در API واکنشگرای آن حالتهای ویژهی fxshow, fxhide, ngclass و ngstyle نیز درنظر گرفته شدهاند که امکان فعالسازی آنها در اندازههای مختلف صفحه مسیر است:
<div fxShow [fxShow.xs]="isVisibleOnMobile()"></div> <div fxHide [fxHide.gt-sm]="isVisibleOnDesktop()"></div> <div [ngClass.sm]="{'fxClass-sm': hasStyle}" ></div> <div [ngStyle.xs]="{color: 'blue'}"></div>
امکان کار با API واکنشگرا از طریق برنامه نویسی
برای این منظور میتوان از سرویس ObservableMedia مانند مثال زیر استفاده کرد:
در اینجا به فعالسازی یک بازهی خاص گوش فرا خواهیم داد. برای مثال اگر اندازهی صفحه xs بود، سبب بارگذاری محتوای خاص مرتبط با موبایل خواهیم شد.
برای مطالعهی بیشتر
خلاصه ng-conf 2017
ما در AngularJs آبجکتی را به نام q$ داریم که برای اجرای توابع به صورت async مفید است و همچنین در استفاده از مقادیر برگشتی از این درخواستها برای پردازشهای آینده به ما کمک میکند. برای اطلاعات بیشتر در مورد این سرویس به اینجا مراجعه کنید.
در ادامه ما از تابع ()all از q$ برای ترکیب چند شیء promise داخل یک شیء promise، به منظور صدا زدن چند سرویس به صورت یکجا، استفاده میکنیم.
پیاده سازی ASP.NET Web API
قدم اول : Visual Studio را بازکنید و یک پروژه empty ASP.NET Web API را مطابق شکل زیر ایجاد کنید.
using System.Collections.Generic; namespace NG_Combine_Multiple_Promises.Models { public class Courses { public int CourseId { get; set; } public string CourseName { get; set; } } public class CourseDatabase : List<Courses> { public CourseDatabase() { Add(new Courses() { CourseId=1,CourseName="الکترونیک"}); Add(new Courses() { CourseId = 2, CourseName = "ریاضی 2" }); Add(new Courses() { CourseId = 3, CourseName = "طراحی نرم افزار" }); } } public class Student { public int StudentId { get; set; } public string Name { get; set; } public string AcadmicYear { get; set; } } public class StudentDatabase : List<Student> { public StudentDatabase() { Add(new Student() {StudentId=101,Name="محمد علوی", AcadmicYear="اول" }); Add(new Student() { StudentId = 102, Name = "طاهره موسوی", AcadmicYear = "دوم" }); Add(new Student() { StudentId = 103, Name = "علی عباسی", AcadmicYear = "سوم" }); Add(new Student() { StudentId = 104, Name = "جواد نوری", AcadmicYear = "اول" }); Add(new Student() { StudentId = 105, Name = "محسن خدایی", AcadmicYear = "دوم" }); Add(new Student() { StudentId = 106, Name = "علی کاظمی", AcadmicYear = "سوم" }); Add(new Student() { StudentId = 107, Name = "زهرا مقدم", AcadmicYear = "اول" }); Add(new Student() { StudentId = 108, Name = "لاله فکور", AcadmicYear = "دوم" }); Add(new Student() { StudentId = 109, Name = "علی نوروزی", AcadmicYear = "چهارم" }); } } }
کد بالا شامل موجودیتهای Courses و Student است و کلاسهای CourseDatabase و StudentDatabase به ترتیب برای ذخیره این موجودیتها است.
قدم سوم : بستهی نیوگت Microsoft.AspNet.WebAPi.Cors را با استفاده از NuGet Package Manager، به منظور فعال سازی امکان صدا زدن این وب سرویس از دامنههای مختلف، به پروژه اضافه کرده و کد زیر را در کلاس WebApiCofig در پوشه App_Start قرار دهید.
config.EnableCors();
قدم چهارم : دو کنترل از نوع Web API 2 Empty را با نامهای CourseAPIController و StudentAPIController ایجاد کرده و کدهای زیر را در آنها قرار دهید.
CourseAPIController.cs
[EnableCors("*","*","*")] public class CourseAPIController : ApiController { [Route("Courses")] public IEnumerable<Courses> Get() { return new CourseDatabase(); } }
StudentAPIController.cs
[EnableCors("*", "*", "*")] public class StudentAPIController : ApiController { [Route("Students")] public IEnumerable<Student> Get() { return new StudentDatabase(); } }
استفاده از Angular $q.all
قدم اول : پروژهی جدیدی را از نوع Empty ASP.NET در همین solution اضافه کرده و ارجاعات jQuery, Bootstrap و AngularJS را با استفاده از NuGet Package manager مانند زیر اضافه کنید:
Install-Package jQuery Install-Package bootstrap Install-Package angularjs
قدم دوم : پوشهایی را به نام MyScripts ایجاد کرده و درون آن فایل javascript زیر را با نام logic.js اضافه کنید:
var app = angular.module('mymodule', []); //سرویسی برای بازگرداندن لیست دروس app.service('courseService', function ($http) { this.get = function () { var response = $http.get("http://localhost:11696/Courses"); return response; }; }); //سرویسی برای بازگرداندن لیست دانشجویان app.service('studentService', function ($http) { this.get = function () { var response = $http.get("http://localhost:11696/Students"); return response; }; }); //تعریف کنترلر app.controller('ctrl', function ($scope, $q, courseService, studentService) { $scope.Courses = []; $scope.Students = []; loadData(); /**/ function loadData() { var promiseCourse = courseService.get(); var promiseStudent = studentService.get(); $scope.combineResult = $q.all([ promiseCourse, promiseStudent ]).then(function (resp) { $scope.Courses = resp[0].data; $scope.Students = resp[1].data; }); } });
<!DOCTYPE html> <html ng-app="mymodule"> <head> <title></title> <meta charset="utf-8" /> <link href="../Content/bootstrap.min.css" rel="stylesheet" /> <script src="../Scripts/jquery-3.2.1.min.js"></script> <script src="../Scripts/angular.min.js"></script> <script src="../MyScripts/logic.js"></script> </head> <body ng-controller="ctrl"> <div class="container"> <h1 class="h1">دروس</h1> <table class="table table-striped table-bordered table-condensed"> <thead> <tr> <td class="text-center">کد درس</td> <td class="text-center">نام درس</td> </tr> </thead> <tbody> <tr ng-repeat="Course in Courses"> <td class="text-center">{{Course.CourseId}}</td> <td class="text-center">{{Course.CourseName}}</td> </tr> </tbody> </table> <hr /> <h1 class="h1">دانشجویان</h1> <table class="table table-striped table-bordered table-condensed"> <thead> <tr> <td class="text-center">کد دانشجویی</td> <td class="text-center">نام و نام خانوادگی</td> <td class="text-center">سال دانشجویی</td> </tr> </thead> <tbody> <tr ng-repeat="Student in Students"> <td class="text-center">{{Student.StudentId}}</td> <td class="text-center">{{Student.Name}}</td> <td class="text-center">{{Student.AcadmicYear}}</td> </tr> </tbody> </table> </div> </body> </html>
- Accessibility improvements in narration, high contrast and focus control areas
- .NET Framework support for .NET Standard 2.0 and compiler features
- More secure SHA-2 support in ASP.NET and System.Messaging
- Configuration builders
- ASP.NET Execution step feature
- ASP.NET HttpCookie parsing
- Enhancements in Visual Tree for WPF applications
- Performance and reliability improvements
CoffeeScript #1
Cookie - قسمت اول
چند تا سوال:
1-چرا expiration مربوط به کوکی رو مرورگر در مراجعات بعدی به سرور نمیفرسته؟اگه لازمش داشته باشیم چه جور باید بدستش بیاریم؟
2-اگه زمان expire مربوط به کوکی برسه کی مسئولیت نابودی cookie رو داره؟
3-مسیر دخیره سازی کوکیها کجا هست؟چه زمانی که IsPersistent برابر true یا false باشه(هر چی میگردم پیداشون نمیکنم)؟
البته مورد 3 رو در استفاده formsauthentication بررسی کردم طبق همون نامی که توی تگ forms توی web.config تنظیم شده.