MSDN Magazine: March 2014 منتشر شد
انتقال به یک صفحه حاوی اطلاعات
- « هدایت خودکار آدرسهای یافت نشد در یک سایت ASP.NET MVC به جستجوی سایت»
»اگر با #F آشنایی ندارید میتوانید از اینجا شروع کنید.
به صورت کلی برای استفاده گسترده از #F در پروژههای وب نیاز به یک سری templateهای آماده داریم در غیر این صورت کار کمی سخت خواهد شد. به تصویر زیر دقت نمایید:
واضح است که با توجه به تصویر بالا کنترلرها و البته مدلهای app و هر آنچه که سمت سرور به آن نیاز است باید با استفاده از #F پیاده سازی شوند. اما هنگامی که کنترلرها با استفاده از #F نوشته شوند سیستم مسیر یابی نیز تحت تاثیر قرار خواهد گرفت. علاوه بر آن باید فکری برای بخش Bundling و همچنین فیلترهاو... نمود. در نتیجه با توجه به template پروژه مورد نظر بر خلاف حالت پیش فرض #C آن که در قالب یک پروژه ارائه میشود در این جا حداقل به دو پروژه نیاز داریم. خوشبختانه همانند پروژه FSharpX که برای WPF مناسب است برای MVC نیز template آماده موجود است که در ادامه با آن آشنا خواهیم شد.
شروع به کار
ابتدا در VS.Net یک پروژه جدید ایجاد نمایید. از بخش Online Template گزینه FSharp MVC 4 را جستجو کنید.
بعد از انتخاب نام پروژه و کلیک بر روی Ok ( و البته دانلود حدود ده MB اطلاعات) صفحه زیر نمایان میشود. در این قسمت تنظیمات مربوط به انتخاب View Engine و نوع قالب پروژه را وجود دارد. در صورتی که قصد استفاده از Web Api را دارید گزینه Web Api Project را انتخاب کنید در غیر این صورت گزینه Empty Project.
البته از Visual Studio 2012 به بعد این بخش به صورت زیر خواهد بود که قسمت Single Page App به آن اضافه شده است:
بعد از کلیک بر روی Ok یک پروژه بر اساس Template مورد نظر ساخته میشود. همانند تصویر زیر:
بررسی تغییرات
در یک نگاه به راحتی میتوان تغییرات زیر را در پروژه Web تشخیص داد:
»پوشه Controller وجود ندارد؛
»پوشه مدل وجود ندارد؛
»فایل Global.asax دیگر فایلی به نام Global.asax.cs را همراه با خود ندارد.
دلیل اصلی عدم وجود موارد بالا این است که تمام این موارد باید به صورت #F پیاده سازی شوند در نتیجه به پروژه #F ساخته شده منتقل شده اند. فایل Global.asax را باز نمایید. سورس زیر قابل مشاهده است:
<%@ Application Inherits="FsWeb.Global" Language="C#" %> <script Language="C#" RunAt="server">
// Defines the Application_Start method and calls Start in // System.Web.HttpApplication from which Global inherits. protected void Application_Start(Object sender, EventArgs e) { base.Start(); } </script>
حال به بررسی پروژه #F ساخته شده خواهیم پرداخت. در این پروژه یک فایل Global.fs وجود دارد که سورس آن به صورت زیر است:
namespace FsWeb open System open System.Web open System.Web.Mvc open System.Web.Routing type Route = { controller : string action : string id : UrlParameter } type Global() = inherit System.Web.HttpApplication() static member RegisterRoutes(routes:RouteCollection) = routes.IgnoreRoute("{resource}.axd/{*pathInfo}") routes.MapRoute("Default", "{controller}/{action}/{id}", { controller = "Home"; action = "Index" id = UrlParameter.Optional } ) member this.Start() = AreaRegistration.RegisterAllAreas() Global.RegisterRoutes(RouteTable.Routes)
RouteConfig.RegisterRoutes(RouteTable.Routes);
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } }
//تعریف الکوی مسیر یابی type Route = { controller : string action : string id : UrlParameter } type Global() = inherit System.Web.HttpApplication() static member RegisterRoutes(routes:RouteCollection) = //فراخوانی و انتساب الگوی مسیر یابی به مسیرهای تعریف شده routes.IgnoreRoute("{resource}.axd/{*pathInfo}") routes.MapRoute("Default", "{controller}/{action}/{id}", { controller = "Home"; action = "Index" id = UrlParameter.Optional } )
Global.RegisterRoutes(RouteTable.Routes)
در این پست با Template پروژههای F# MVC 4 اشنا شدیم و از طرفی مشخص شد که برای پیاده سازی این گونه پروژهها حداقل نیاز به دو پروژه داریم. یک پروژه که از نوع #C است ولی در آن فقط View ها و فایل جاوااسکریپتی و البته Css وجود دارد. از طرف دیگر کنترلرها و مدلها و هر چیز دیگر که مربوط به سمت سرور است در قالب یک پروژه #F پیاده سازی میشود.
نصب وب سرور آپاچی
به اینترنت متصل شده، ترمینال Ubuntu را گشوده (با میانبر ctrl+alt+t) و سپس فرمانهای ذیل را صادر کنید:
sudo apt-get update sudo apt-get upgrade -y sudo apt-get install apache2
نصب ماژولهای ASP.NET مخصوص آپاچی
سپس نیاز است ASP.NET runtime for Apache را نصب کنیم:
sudo apt-get install libapache2-mod-mono mono-apache-server4
اگر علاقمند به مشاهده تنظیمات آن بودید باید به مسیر etc/apache2/mods-enabled مراجعه کرده و فایل mod_mono.conf را بررسی کنید (اختیاری). برای مثال، در آن حالت اجرا، بر روی ASP.NET 4 تنظیم شدهاست.
تنظیمات آپاچی برای کار با ASP.NET
نیاز است فایل تنظیمات پیش فرض وب سرور آپاچی را جهت معرفی ASP.NET به آن، اندکی ویرایش کنیم:
sudo gedit /etc/apache2/sites-available/default
MonoAutoApplication disabled AddHandler mono .aspx .ascx .asax .ashx .config .cs .asmx .axd MonoApplications "/:/var/www" MonoServerPath default "/usr/bin/mod-mono-server4"
SetHandler mono DirectoryIndex index.aspx index.html default.aspx Default.aspx
<DirectoryMatch "/([bB]in|[Aa]pp_[Cc]ode|[Aa]pp_[Dd]ata|[Aa]pp_[Gg]lobal[Rr]esources|[Aa]pp_[Ll]ocal[Rr]esources)/"> Order deny,allow Deny from all </DirectoryMatch>
sudo service apache2 restart
# r,w,exec for user + group sudo chgrp -R www-data /var/www sudo chown -R www-data /var/www sudo chown -R www-data:www-data /var/www sudo chmod -R 755 /var/www
gedit /var/www/index.html
اجرای مثالها
با تنظیمات فوق، برنامههای کپی شده در مسیر var/www به کمک مونو و آپاچی اجرا خواهند شد.
دسترسیهای فعلی کاربر وارد شده به لینوکس اجازه کپی فایلها را به مسیر var/www نمیدهد. همچنین میخواهیم این کارها را توسط File browser آن انجام دهیم و نه خط فرمان. برای این منظور دستور ذیل را اجرا کنید تا File browser آن با دسترسی مدیریتی اجرا شود:
sudo nautilus
پس از کپی دو برنامه Web forms و MVC ابتدای بحث، نیاز است مجددا فایل تنظیمات آپاچی را ویرایش کنیم:
sudo gedit /etc/apache2/sites-available/default
AddMonoApplications default "/webforms_test:/var/www/WebFormsApp/" <Location /webforms_test> SetHandler mono </Location>
و تنظیم برنامه MVC به صورت زیر میباشد:
AddMonoApplications default "/mvc_test:/var/www/Mvc4ProjectApp/" <Location /mvc_test> SetHandler mono </Location>
اکنون برنامه وب فرمها در مسیر http://127.0.0.1/webforms_test و برنامه MVC در مسیر http://127.0.0.1/mvc_test قابل دسترسی است.
خلاصه بحث
پس از نصب وب سرور آپاچی و ماژول مونوی مخصوص آن، فایل etc/apache2/sites-available/default را به نحو ذیل ویرایش کنید و مسیرهای برنامههای خود را در آن تعریف نمائید:
<VirtualHost *:80> ServerAdmin webmaster@localhost MonoAutoApplication disabled AddHandler mono .aspx .ascx .asax .ashx .config .cs .asmx .axd MonoApplications "/:/var/www" MonoServerPath default "/usr/bin/mod-mono-server4" DocumentRoot "/var/www" <Directory /> Options FollowSymLinks AllowOverride None </Directory> <Directory /var/www/> SetHandler mono DirectoryIndex index.aspx index.html default.aspx Default.aspx Options Indexes Includes FollowSymLinks MultiViews AllowOverride None Order allow,deny allow from all </Directory> ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ <Directory "/usr/lib/cgi-bin"> AllowOverride None Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch Order allow,deny Allow from all </Directory> <DirectoryMatch "/([bB]in|[Aa]pp_[Cc]ode|[Aa]pp_[Dd]ata|[Aa]pp_[Gg]lobal[Rr]esources|[Aa]pp_[Ll]ocal[Rr]esources)/"> Order deny,allow Deny from all </DirectoryMatch> AddMonoApplications default "/webforms_test:/var/www/WebFormsApp/" <Location /webforms_test> SetHandler mono </Location> AddMonoApplications default "/mvc_test:/var/www/Mvc4ProjectApp/" <Location /mvc_test> SetHandler mono </Location> ErrorLog ${APACHE_LOG_DIR}/error.log # Possible values include: debug, info, notice, warn, error, crit, # alert, emerg. LogLevel warn CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost>
حالتهای مختلف ذخیره سازی اطلاعات در مرورگر کاربر
Web Storage و یا Client-side storage در دو حالت کلی session storage و local storage قابل دسترسی است:
الف) session storage
در این حالت اطلاعات ذخیره شدهی در session storage، پس از بسته شدن مرورگر، به صورت خودکار حذف خواهند شد.
ب) local storage
اطلاعات ذخیره شدهی در local storage پس از بسته شدن مرورگر نیز باقی مانده و قابل دسترسی و بازیابی مجدد هستند. تاریخ انقضای آنها صرفا بر اساس خالی شدن دستی کش مرورگر توسط کاربر و یا حذف دستی اطلاعات آن توسط کدهای برنامه تعیین میشود.
هر دو حالت فوق به صورت ایزوله ارائه میشوند؛ با محدودیت حجم 10 مگابایت (جمع حجم نهایی هر دو حالت با هم، محدود به 10 مگابایت است). به این معنا که برنامههای هر دومین، تنها به محل ذخیره سازی خاص همان دومین دسترسی خواهند داشت.
همچنین API دسترسی به آنها synchronous است و کار کردن با آنها سادهاست.
البته Client-side storage به دو مورد فوق خلاصه نمیشود و شامل File Storage ،WebSQL ،IndexedDB و کوکیهای مرورگر نیز هست.
- File Storage هنوز مراحل آزمایشی خودش را طی میکند و مناسب برنامههای دنیای واقعی نیست.
- WebSQL قرار بود بر اساس بانک اطلاعاتی معروف SQLite ارائه شود؛ اما W3C در سال 2010 این استاندارد را منسوخ شده اعلام کرد و با IndexedDB جایگزین شد. دسترسی به آن async است و میتواند موضوع بحثی مجزا باشد.
- کوکیهای مرورگرها نیز یکی دیگر از روشهای ذخیره سازی اطلاعات در مرورگرها هستند و تنها به ذخیره سازی حداکثر 4096 بایت اطلاعات محدود هستند. کوکیها نیز همانند local storage پس از بسته شدن مرورگر باقی میمانند؛ اما برخلاف آن، دارای تاریخ انقضاء و همچنین قابلیت ارسال بین دومینها را نیز دارا میباشند. اگر تاریخ انقضای یک کوکی تعیین نشود، همانند session storage، در پایان کار مرورگر و بسته شدن آن، حذف خواهد شد.
تهیه یک سرویس Angular برای کار با Web Storage
جهت کپسوله سازی نحوهی کار با session storage و local storage میتوان سرویسی را برای اینکار تهیه کرد:
import { Injectable } from "@angular/core"; @Injectable() export class BrowserStorageService { getSession(key: string): any { const data = window.sessionStorage.getItem(key); return JSON.parse(data); } setSession(key: string, value: any): void { const data = value === undefined ? null : JSON.stringify(value); window.sessionStorage.setItem(key, data); } removeSession(key: string): void { window.sessionStorage.removeItem(key); } removeAllSessions(): void { for (const key in window.sessionStorage) { if (window.sessionStorage.hasOwnProperty(key)) { this.removeSession(key); } } } getLocal(key: string): any { const data = window.localStorage.getItem(key); return JSON.parse(data); } setLocal(key: string, value: any): void { const data = value === undefined ? null : JSON.stringify(value); window.localStorage.setItem(key, data); } removeLocal(key: string): void { window.localStorage.removeItem(key); } removeAllLocals(): void { for (const key in window.localStorage) { if (window.localStorage.hasOwnProperty(key)) { this.removeLocal(key); } } } }
در حالت setItem اطلاعاتی را که مرورگرها ذخیره میکنند باید رشتهای باشد. به همین جهت توسط متد JSON.stringify میتوان یک شیء را تبدیل به رشته کرد و ذخیره نمود و در حالت getItem توسط متد JSON.parse، میتوان این رشته را مجددا به همان شیء پیشین خود تبدیل کرد و بازگشت داد.
محل صحیح تعریف BrowserStorageService
همانطور که در مطلب «سازماندهی برنامههای Angular توسط ماژولها» بررسی شد، محل صحیح تعریف این سرویس سراسری مشترک در بین کامپوننتها و ماژولهای برنامه، در CoreModule و پوشهی src\app\core\browser-storage.service.ts است:
import { BrowserStorageService } from "./browser-storage.service"; import { NgModule } from "@angular/core"; import { CommonModule } from "@angular/common"; import { RouterModule } from "@angular/router"; @NgModule({ imports: [CommonModule, RouterModule], exports: [], // components that are used in app.component.ts will be listed here. declarations: [], // components that are used in app.component.ts will be listed here. providers: [BrowserStorageService] // singleton services of the whole app will be listed here. }) export class CoreModule { };
و CoreModule نیز به AppModule اضافه میشود:
import { CoreModule } from "./core/core.module"; @NgModule({ imports: [ //... CoreModule, //... RouterModule.forRoot(appRoutes) ], //... }) export class AppModule { }
بنابراین یکی دیگر از روشهای به اشتراک گذاری اطلاعات در بین قسمتهای مختلف برنامه، ذخیره سازی آنها در session/local storage و سپس بازیابی آنها بر اساس کلیدهای مشخص آنها است.
مثالی از نحوهی کاربرد BrowserStorageService
برای آزمایش سرویس تهیه شده، از کامپوننت و قالب ذیل استفاده خواهیم کرد. در اینجا سرویس BrowserStorageService به سازندهی کلاس تزریق شدهاست و سپس دو حالت session storage و local storage مورد بررسی قرار گرفتهاند:
import { BrowserStorageService } from "./../../core/browser-storage.service"; import { Component, OnInit } from "@angular/core"; @Component({ selector: "app-browser-storage-sample-test", templateUrl: "./browser-storage-sample-test.component.html", styleUrls: ["./browser-storage-sample-test.component.css"] }) export class BrowserStorageSampleTestComponent implements OnInit { fromSessionStorage = ""; fromLocalStorage = "" sessionStorageKey = "sessionStorageKey1"; localStorageKey = "localStorageKey1" constructor(private browserStorage: BrowserStorageService) { } ngOnInit() { } sessionStorageSetItem() { this.browserStorage.setSession(this.sessionStorageKey, "Val1"); } sessionStorageGetItem() { this.fromSessionStorage = this.browserStorage.getSession(this.sessionStorageKey); } localStorageSetItem() { this.browserStorage.setLocal(this.localStorageKey, { key1: "val1", key2: 2 }); } localStorageGetItem() { this.fromLocalStorage = JSON.stringify(this.browserStorage.getLocal(this.localStorageKey)); } }
<h1>Browser storage sample</h1> <div class="panel"> <button class="btn btn-primary" (click)="sessionStorageSetItem()" type="button">sessionStorage -> Set Item</button> <button class="btn btn-success" (click)="sessionStorageGetItem()" type="button">sessionStorage -> Get Item</button> <div class="alert alert-info" *ngIf="fromSessionStorage"> {{fromSessionStorage}} </div> </div> <div class="panel"> <button class="btn btn-warning" (click)="localStorageSetItem()" type="button">localStorage -> Set Item</button> <button class="btn btn-success" (click)="localStorageGetItem()" type="button">localStorage -> Get Item</button> <div class="alert alert-info" *ngIf="fromLocalStorage"> {{fromLocalStorage}} </div> </div>
در این حالت اگر برنامه را اجرا کنیم، یک چنین خروجی قابل مشاهده خواهد بود:
و اگر به برگهی Application کنسول ابزارهای توسعه دهندههای مرورگرها نیز مراجعه کنیم، این مقادیر ثبت شده را در دو حالت استفادهی از session storage و local storage، میتوان مشاهده کرد:
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.