2013年6月29日 星期六

BI導論

一、BI的趨勢:
  1. 員工之間在使用BI工具上的成果分享
  2. 績效管理的成果會在使用的過程中漸趨成熟
  3. 大公司對於導入BI並不積極,但是小公司卻勇於嘗試
  4. 將會採用集中式管理
二、痛苦的經驗
  1. 資料品質不佳
  2. 資料來源過於分散
  3. 資料格式不一
三、公司購買BI的主要考量因素
  1. 工具的價格
  2. 是否易於使用
  3. 能否與現有企業內部基礎建設作整合
  4. 供應商的技術支援能力
  5. 與市面軟體的整合度
  6. BI工具的系統穩定度
四、績效管理
  1. 設定績效目標然後進行監控,讓決策者知道目前正朝著訂定的目標前進。
  2. 制定KPI並不容易,因為每個人的KPI都不相同,每個不同的公司也有不同的KPI,但若制定不當則會造成績效管理上的問題。
  3. 主要推行的障礙,衡量標準的制定是最困難的部份,其次則為無法產生衡量標準以及缺少KPI的衡量和監控,還有無法決定適當的KPI。
五、企業級系統的趨勢
image
排名為: BI –> CRM –> ERP –> SCM
在各區域方面,中國和韓國的ERP較高但是BI也不少
而歐洲各國則BI相當高,這是因為這些國家的ERP已經相當成熟。
六、BI的定義
可將BI比喻成一個煉油廠,其圖型如下:
image
從原始資料到資料倉儲的資訊:
從企業間交易和內部系統中去萃取資料,經過清洗、轉換等處理步驟,將定義清楚且一致的細節和彙總的資料,載入到資料倉儲的資料庫中,從最底層的資料轉換成資料倉儲的資訊。
從資訊到知識:
運用各種報表及分析工具存取並分析資料倉儲中的資訊。從分析中可以找出資料中的趨勢、型態、和例外狀況等,這些分析工具幫助使用者將資訊轉換成知識。
從知識到決策:
從分析所發現的趨勢和型態中可以建立業務規則,也可以將知識作為建立決策模型的依據,來規劃業務的進行並作為決策的參考。統計分析和最佳化分析也可以產生比較複雜的規則。
從決策到行動:
根據前一步的業務規則或決策規劃,使用者要產生執行計劃,這些執行計劃將決策方案轉換成實際的行動。
回饋迴圈:
一旦計劃開始實施,整個循環就會開始進行。將反饋的資料萃取出來和相關資料整合後載入資料倉儲,並且進一步分析執行成效後加以修正規劃。
七、BI在業務以及資訊技術方面的整合
BI是一個資訊技術促成的環環相扣的企業流程。BI需要整合業務和資訊技術的專業,其導入必須採用社會技術的方法(Sociao-technical approach)。
為了解決業務單位和資訊單位中間的Gap,有可能需要進行調整與讓步:
  1. 資訊單位調整->軟體客制化
  2. 業務單位調整->企業流程的改造
八、BI的組成
資料倉儲由資訊單位主導,工作任務是從多個系統中的交易資料進行萃取,經過清理、建模、轉換、和移轉的過程,最後載入到資料倉儲為止。
資訊分析環境由企業中的使用者主導,使用各種分析工具對資料倉儲中的資訊進行查詢、製作報表、分析、採礦(mining)、和視覺化等工作,最重要的是依據查詢或分析結果採取行動。

2012年11月29日 星期四

ASP.Net MVC中的WebAPI

image

     ASP.Net MVC在第四版,也就是MVC 4提供了一個嶄新的東西-WebAPI。其實這個東西早在以前的WCF就推出了,只是當時在整個WCF中它僅是其中的一個分支,而在WCF中它是一種Http服務,但畢竟WCF的龐大是眾所周知,而區區一個Http 服務在WCF眼裡看來當然顯得有些微不足道。在當時,Http並非是WCF的唯一新寵但是卻也讓大家耳目一新,不過在大家興高采烈的想要馬上做幾個範例時,在複雜的組態檔設定面前,大家都卻步了。

     Http服務是一種趨勢,它原本是想要試著去做到REST架構,提供開發人員與前端設計人員以一種方便又快速的方式存取後端服務所提供的資源,但是Html5襲捲全球開發人員之後Http服務更是水漲船高。微軟在Visual Studio的設計上從以前到現在都是一直朝著讓開發人員便利且快速開發專案為目標,而這位未來的新寵兒勢必要有一個快速且便利開發的管道,這樣才能擄獲開發人員的心。戴太陽眼鏡

      MVC4的推出委實是一個震撼彈,在當初Preview時它內容之豐富實在是讓人咋舌,其中最讓大家瘋狂的一項特性就是WebAPI。這個前身為Http服務的傢伙晃著晃著居然加入了MVC中,雖然一開始讓人很不解,但是打開它的程式來看就一豁然開朗了。WebAPI在開發簡直就和Controller開發如出一轍,唯一比較不同的地方在於WebAPI它回傳的東西和Controller不一樣,除此之外,它的開發方式以及Filter的套用嚴然就是一個Controller的變種,雖是如此,但WebAPI還是WebAPI它仍沒有改變它是Http服務的本質。

      Http是一種公開的標準,目前在Html5持續媚力燃燒的狀況下Http的應用推陳出新,利如像是影像串流-MotionJPEG以及REST架構…等等。而目前不論手持式裝置或是網頁開發,幾乎都和Http脫離不了關係,為了讓手持式裝置的APP或是網頁的資料總是能夠提供最新資訊,Http就成了最佳的資料獲取管道。或許大家會很好奇,為什麼不是採用目前網路常用的協定,諸如:TCP或是UDP呢?其實,這兩種協定一直都是資料傳輸的老字號,但是並不是所有場合都適用,更甚至在某些場合下是無法使用的,為了避免這種開發上的限制,毫無拘束的Http反倒成了資料傳輸的新寵兒。

     在MVC4專案中,App_Start資料夾底下的WebApiConfig.cs檔案,其實裡面幾乎和同資料夾的RouteConfig.cs一樣,其目的都是在做Routing(路由)設定的工作,我們來看看WebApiConfig.cs的程式碼:
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }

     而RouteConfig.cs則為:

        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 }
            );
        }

   觀其兩者的差異,我們可以從url這個參數中發現。在WebApiConfig中,url的部份是api/{controller}/{id},而RouteConfig則是{controller}/{action}/{id}。而以下範例可以區分誰會被導向WebAPI處理,誰會被導向一般Controller處理。

  兩者的差別就在於Url路徑中WebAPI會特別寫上api這三個小寫英文字。

  接著我們就來實際新增一個WebAPI看看其中的程式內容。

image

image

image

   我們來觀察一下具有空白讀取/謝入動作的API控制器其程式碼內容為何。
    public class DefaultController : ApiController
    {
        // GET api/default
        public IEnumerable Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/default/5
        public string Get(int id)
        {
            return "value";
        }

        // POST api/default
        public void Post([FromBody]string value)
        {

        }

        // PUT api/default/5
        public void Put(int id, [FromBody]string value)
        {

        }

        // DELETE api/default/5
        public void Delete(int id)

        {

        }
    }

   WebAPI遵循REST架構,它利用Http的其中四個動詞(Verb)來對應資料庫操作的新增/刪除/修改/查詢。其對應如下:
  • Get => 查詢
  • Post => 新增
  • Put => 修改
  • Delete => 刪除

        而DefaultController中的內容,其每個方法的上頭都有一對註解,其註解內容就可以大致窺見WebAPI每個方法其Url大致的輪廓。比較特別的地方就在於Post和Put這兩個方法的參數,其參數中代有屬性標籤: FromBody。這個屬性標籤指的是:該參數是由MessageBody這個類別內容自動取得,也就是說,當Request送過來就會自動從其Body中擷取內容變數value的值出來。

       大家應該有個疑問,WebAPI很好用阿而開發模式也和Controller一模一樣,完全省去了WCF煩鎖的組態地獄但卻提供了相同的功用和效果,但是…它和Controller到底是用在什麼場合;什麼場合該用Controller什麼場合該用WebAPI。其實這個問題我曾經想過,後來得到twMVC核心成員-Demo的解答。


總結:WebAPI是一個好用的利器,雖然它被鑲嵌在MVC4中,但是不代表它只能使用MVC4範本來開發,它的本質仍是一種寄宿型服務,它目前僅是寄宿在MVC身上,想當然它也可以自由寄宿在某個Process身上。至此,App_Start這個資料夾的探索已經結束了,再來的章節就是介紹MVC4範本中每個JavaScript的功用即其應用場合。

2012年11月27日 星期二

ASP.Net MVC中的Bundle

image

    ASP.Net MVC在第四版提供了一個貼心的工具-Bundle。而這個工具對於所有網頁開發的人員來說實在是一個至關重要的工具。一個網站除了功能和畫面UI能夠做到吸引人之外,更重要的地方就是網站要能夠讓更多人能夠瀏覽,否則再好的內容若總是只能幾隻小貓進來看,那豈不是太可惜了呢。

     影響一個網站能否讓更多人瀏覽有幾個重點:
  • 功能符合時下大眾需求
  • 畫面能夠第一時間吸引人
  • 友善的操作介面
  • 提供流暢的操作體驗而不會有頓感

    上述的每項牽涉的層面都非常廣泛,但是和今天要介紹的Bundle最息息相關的就屬第四項。網站要能夠提供流暢的操作體驗而不會讓使用者有頓感,其實就是在控制頁面響應能力;當使用者進入網頁或是點選某一個選單功能,結果就看到網頁凍結在那邊等著網站的回應,我想任何使用者都會逐漸感到不耐進而不想再繼續瀏覽下去。具所有開發人員的認知,一個頁面的響應時間不應該超過4秒,但是老實說這個4秒其實對於使用者來說是最大能夠忍受的極限,因此,不能將4秒作為網站響應時間的最終目標,而是應該將其作為危險指標。

     控制網頁響應時間的因素有很多,我大約羅列如下:
  • 後端/前端 程式沒有妥善控制資源(例如沒有關閉SqlConnection或無窮迴圈)
  • 沒有檢查好網站資源(例如使用者端向伺服器端要一張不存在的圖片)
  • 網頁太肥
  • 單一頁面需要向伺服器端索取多個資源檔

    在上述的因素中,其中的最後一項正是Bundle可以減緩的,為什麼只說可以減緩呢?因為Bundle僅能對於網站的JavaScript及CSS檔案做出處理,它的實際作用就是將網站上一堆的JavaScript或是CSS都打包成單一*.js或是*.css檔,如此一來,使用者端就不用開多個連線向伺服器端索取一堆的JavaScript以及CSS檔案了。這在某個程度上可以減緩索取多個資源檔所造成的連線損耗。

    使用者端發出一個Request給伺服器端,伺服器端在接收到這個Request之後,它會需要有一個執行緒去處理這個Request,為什麼要使用執行緒呢?這是因為網站本身的目的就是要能夠在同一時間盡可能的服務多個使用者的Request,為了達成這個目的,使用執行緒是勢在必行。其實,一台伺服器的執行緒個數是有限的,因為一個執行緒是會消耗掉近2MB的記憶體,而硬體資源不可能無限大但是使用者的數量卻可能無限上綱,為了能夠盡可能的服務多個使用者的Request,妥善的控制連線數也成了一個重要的課題。

     MVC 4所提供的Bundle的功能旨在打包JavaScript和CSS,除此之外,它還有一項更迷人的功能-壓縮。
    這裡所指的壓縮是指將原本很龐大的內容藉由將空白字元以及換行字元消滅掉之外,它還將JavaScript中的變數名稱替換成更簡單的a,b,c,d…這種單一字母,這是因為當字數越少則這個檔案相對來說就會更小,如此一來就做到了所謂實質上的壓縮功能。

    除了上述所探討到的執行緒問題之外,頻寬也是一個重要的指標,但是頻寬可能涉及到的層面很廣,除了JavaScript和CSS之外,當然免不了還有網頁的大小以及網頁上其它資源檔的大小,這些都是影響頻寬的因素,而Bundle不是萬靈丹,它僅能針對它能控制的JavaScript和CSS檔案的大小作文章之外,其餘部份它是愛莫能助的。

    接下來我們來看看App_Start資料夾底下的BundleConfig.cs檔案簡略內容:

    public class BundleConfig
    {
        // 如需 Bundling 的詳細資訊,請造訪 http://go.microsoft.com/fwlink/?LinkId=254725
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                        "~/Scripts/jquery-{version}.js"));

            bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
                        "~/Scripts/jquery-ui-{version}.js"));

            bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                        "~/Scripts/jquery.unobtrusive*",
                        "~/Scripts/jquery.validate*"));

            // 使用開發版本的 Modernizr 進行開發並學習。然後,當您
            // 準備好實際執行時,請使用 http://modernizr.com 上的建置工具,只選擇您需要的測試。
            bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
                        "~/Scripts/modernizr-*"));

            bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));

            bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
                        "~/Content/themes/base/jquery.ui.core.css",
                        "~/Content/themes/base/jquery.ui.resizable.css",
                        "~/Content/themes/base/jquery.ui.selectable.css",
                        "~/Content/themes/base/jquery.ui.accordion.css",
                        "~/Content/themes/base/jquery.ui.autocomplete.css",
                        "~/Content/themes/base/jquery.ui.button.css",
                        "~/Content/themes/base/jquery.ui.dialog.css",
                        "~/Content/themes/base/jquery.ui.slider.css",
                        "~/Content/themes/base/jquery.ui.tabs.css",
                        "~/Content/themes/base/jquery.ui.datepicker.css",
                        "~/Content/themes/base/jquery.ui.progressbar.css",
                        "~/Content/themes/base/jquery.ui.theme.css"));
        }
    }
   上述程式碼我們可以摸索出一個簡單的處理模式;當你想要針對JavaScript進行打包的時候,你只需要將ScriptBundle物件實體化並加入到bundles物件中,而CSS檔案則是將StyleBundle物件加入到bundles中。唯一幾個比較特別的程式碼如下:

bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                        "~/Scripts/jquery-{version}.js"));
        在上述程式碼中我們可以看到有一個很醒目的東西: {version}。其實這個東西是一個特別的參數,而這是Bundle制定的,該參數的意思是指任何版本。jQuery是一個非常好用且熱門的JavaScript框架套件,以目前的網頁開發來說,不論是網頁設計師或是後端網站的程式設計師,皆要對這個框架套件非常熟才行,在過去,會jQuery是一件令人稱羨的事,而現在會jQuery卻成了必備的技能。jQuery從開始到現在已經有多個版本問世,如果每更新一次jQuery的新版就要去修改BundleConfig.cs,那豈不是太累人了,為此,便提供了像是{version}這種參數讓大家可以更方便的使用Bundle功能。

        更多的Bundle參數可以在這個網址中找到資源。

       我們現在就打開IE並且按下F12啟動檢測工具來看看Bundle的成效。

image

    咦!怎麼好像還是跑到原路徑去抓CSS和JavaScript檔案呢?其實,Bundle這項功能的啟動時機非常特別,它只有在非Debug模式下才能啟用,如果沒有改成非Debug模式,則Bundle則是毫無作用的。其目的很簡單,當你處於Debug模式時,表示你目前正在對該網站進行偵錯,而偵錯的目標很可能是某個自己撰寫的JavaScript,若這個檔案被Bundle壓縮的你完全看不懂,那要如何偵錯呢?

     接下來我們來將網站改成非Debug模式

image

    除了將原本的Debug改成Release之外,還要去修改網站的Web.config。(不是Views資料夾底下的那個歐)

<compilation debug="false" targetFramework="4.5"/>

   我們再來看一次網頁。(不偵錯啟動要按下: Ctrl+F5)

image

總結: Bundle是一個非常重要的功能,在MVC 4中,它的撰寫異常的簡單,為了這份簡單大家記得一定要去善用它歐。

2012年11月26日 星期一

ASP.Net MVC中的Filter

image

     MVC中的Filter對於整體開發來說總是有著畫龍點睛的效果;一個網站中的Filter並不會很多,但是每個Filter總是能夠完美的發揮它關鍵性的角色,它其實是一個很特別的東西,你可以決定將它套用到整個網站,或是細到某個Action。無論是使用MVC目前現成的Filter或是自行開發Filter,套用Filter其實在簡單不過,因為只需要將其當成一般的屬性標籤,直接將它放在某個Controller或是某個Action上頭就能達到效果。

     Filter如果是想要套用到整個網站,需進行Global的註冊行為,其位置就在App_Start資料夾底下的FilterConfig.cs中,我們打開FilterConfig.cs來看看裡面的內容。

    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }
    }

    在靜態方法RegisterGlobalFilters中,僅有一行程式碼,該程式就是將HandleErrorAttribute這個MVC提供的Filter進行Global的註冊,亦即,HandleError這個Filter會套用到整個網站。

  在MVC的專案中,我們可以看到有一個資料夾名為:Filters,該資料夾裡面所放置的就是開發人員自行撰寫的Filter。在預設範本程式碼中,我們可以看到Filters資料夾裡面有一個InitializeSimpleMember.cs檔案,打開該檔案其程式碼如下:

   [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
   public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute
   {

       private static SimpleMembershipInitializer _initializer;

       private static object _initializerLock = new object();

       private static bool _isInitialized;

       public override void OnActionExecuting(ActionExecutingContext filterContext)
       {
           // Ensure ASP.NET Simple Membership is initialized only once per app start
           LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
       }

       private class SimpleMembershipInitializer
       {
           public SimpleMembershipInitializer()
           {
               Database.SetInitializer(null);

               try
               {
                   using (var context = new UsersContext())
                   {
                       if (!context.Database.Exists())
                       {
                           // Create the SimpleMembership database without Entity Framework migration schema

                           ((IObjectContextAdapter)context).ObjectContext.CreateDatabase();

                       }
                   }

                   WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
               }

               catch (Exception ex)
               {
                   throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);

               }
           }
       }
   }

       該程式碼的目的很簡單:當資料庫檔案不存在的時候,自動建立一個資料庫檔案供會員管理使用。由此解了我們前幾篇曾經提到的疑問,為什麼按下了網頁上的註冊之後,在App_Data的實體路徑下會多出兩個和資料庫相關的檔案。其原由就是出在這個Filter。而這個Filter套用在AccountController。

    [Authorize]
    [InitializeSimpleMembership]
    public class AccountController : Controller
    {

            …省略…
    }

        所以當使用者按下了會與AccountController產生戶動的任何頁面元件時,這個Filter就會自動啟動並進行一連串的動作。其實自行撰寫MVC的Filter很簡單,但是要先來瞭解MVC的Filter其內部的運作模式,這樣一來就不會完全摸不著頭緒了。

      其實,一個Action從Request被Router導過來一直到Action處理完畢並將最後一棒交遞給View,其流程圖如下:


actionfilter2_thumb
 
      我們可以看到幾個較特別的步驟
  • OnActionExecuting(Action處理前)
  • OnActionExecuted(Action處理後)
  • OnResultExecuting(結果View處理前)
  • OnResultExecuted(結果View處理後)

    在Filter的開發上,主要有四大類,而上述的四個步驟就是其中兩大類要處理的目標。
  • Authorization(針對驗證/授權處理)
  • Action(針對Action處理時機作特製: Executing/Executed)
  • Result(針對View處理時機作特製: Executing/Executed)
  • Exception(針對例外作處理)

    這四大類各有各自要實作的介面,下圖描述那一類該實作那種介面:

actionfilter_2
 
     要開發一個自定義的Filter,僅需繼承ActionFilter再依自己要針對四大類那一類進行處理就去實作該類的介面。舉個例來說,如果我想要自行定義一個驗證相關的ActionFilter,則程式碼概略如下:

public class LogAttibute:ActionFilterAttribute,IActionFilter
{
     public override void OnActionExecuting(ActionExecutingContext filterContext)
     {  …省略… }
}

   而要套用這個自定義的Filter很簡單;若要是想要套用在HomeController上,則簡略程式碼如下:

[LogAttribute]
public class HomeController : Controller
{ …省略…}

    若想要將這個自定義的Filter套用到全網站,則需要App_Start資料夾底下的FiltersConfig.cs中加入以下程式碼:
 
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());

            filters.Add(new LogAttribute());
        }
    }

總結:在MVC中Filter是一個不可或缺的工具,善用它可以事半功倍。
 
  

2012年11月25日 星期日

ASP.Net MVC中的OAuth

image

         撰寫任何公開的系統都需要有適當的驗證/授權機制作為安全性控管。在ASP.Net MVC 4中,提供了相當完整的機制,除了預設的表單驗證之外,還加入了一個令人振奮的特性-OAuth。而本篇文章將以大家最常會使用到的Facebook作為OAuth的範例作說明。

       打開App_Start底下的AuthConfig.cs這個檔案來看,我們會發現到裡面的內容幾乎全部都被註解了

    public static class AuthConfig
    {

        public static void RegisterAuth()
        {

            // 若要讓此網站的使用者使用其他網站 (如 Microsoft、Facebook 和 Twitter) 的帳戶登入,

            // 您必須更新此網站。如需詳細資訊,請造訪 http://go.microsoft.com/fwlink/?LinkID=252166

            //OAuthWebSecurity.RegisterMicrosoftClient(

            //    clientId: "",

            //    clientSecret: "");

            //OAuthWebSecurity.RegisterTwitterClient(

            //    consumerKey: "",

            //    consumerSecret: "");

            //OAuthWebSecurity.RegisterFacebookClient(

            //    appId: "",

            //    appSecret: "");

            //OAuthWebSecurity.RegisterGoogleClient();
        }
    }
 
從註解的內容我們發現到一個事實,RegisterAuth這個靜態方法的內容具有四個很快就被人觀注的四個東西:
  • RegisterMicrosoftClient
  • RegisterTwitterClient
  • RegisterFacebookClient
  • RegisterGoogleClient
     從上述所列的名稱可以更進一步推敲,這似乎是在針對目前時下最廣為大家使用的四個社群網站:Microsoft, Twitter, Facebook, Google。再來我們回到這些方法的類別: OAuth。
OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。
      上述是我引用Wiki對於OAuth的描述,老實說,我還真是看不太懂它的內容。吐舌頭不過我們可以使用另一種較快速吸收的方式來解釋它:利用目前現成的社群網站所提供的驗證服務來對使用者進行驗證。當然,OAuth所提供的能力當然不僅僅於此,甚至可以說,用了它之後就可以對使用者提供更多的服務,例如像是可以取得使用者在Facebook上的一些資訊,而這些資訊可以讓網頁呈現的更豐富也更能讓使用者快速上手,畢竟使用者已經很習慣Facebook的操作介面或是資料呈現,如果自己所提供的網站與Facebook有更多的重疊性,就能提高使用者的接受度。

    在本篇文章中,主要針對的部份是驗證/授權,因此,所需要涵蓋的範圍也比較廣,除了要介紹AuthConfig.cs中的內容之外,也必需要介紹Controllers資料夾底下的AccountController。首先先來看一下AccountController在處理表單驗證的部份。

[Authorize]
    [InitializeSimpleMembership]
    public class AccountController : Controller
    {
        //
        // GET: /Account/Login

        [AllowAnonymous]
        public ActionResult Login(string returnUrl)
        {

            …省略…
        }

        //
        // POST: /Account/Login
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Login(LoginModel model, string returnUrl)
        {

            …省略…
        }

        //
        // POST: /Account/LogOff
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult LogOff()
        {
            …省略…}

        //
        // GET: /Account/Register
        [AllowAnonymous]
        public ActionResult Register()
        {
           …省略…        
        }

        //
        // POST: /Account/Register
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Register(RegisterModel model)
        {
            …省略…
        }

        //
        // POST: /Account/Disassociate
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Disassociate(string provider, string providerUserId)
        {
            …省略…
        }

        //
        // GET: /Account/Manage
        public ActionResult Manage(ManageMessageId? message)
        {
            …省略…        
        }

        //
        // POST: /Account/Manage
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Manage(LocalPasswordModel model)
        {
            …省略…
        }

}

        AccountController主要有兩大部份,一個是針對表單驗證的部門,另一個則是針對OAuth驗證的部份,而上述程式碼僅是針對表單驗證的部份。在表單驗證的部份一共有下列方法:
  • Login(登入)
  • LogOff(登出)
  • Register(註冊)
  • Disassociate(刪除帳號與第三方網站的關聯)
  • Manage(變更帳戶密碼)

       某些方法有Get與Post分別,所以會在上述程式碼中看到某幾個方法會重覆出現,但是該重覆的方法會有一個很明顯的特徵就是其方法上頭會有一個[HttpPost]的屬性標籤。MVC4與MVC3在AccountController上有很大的不同,除了多了另一個部份是在處理OAuth之外,方法的屬性標籤也變得更多了,茲羅列MVC4在AccountController用到那些屬性標籤:
  • Authorize(需通過授權才能使用)
  • InitializeSimpleMembership(初始化帳戶管理相關機制)
  • AllowAnonymous(在執行Action時,略過驗證/授權的處理)
  • ValidateAntiForgeryToken(避免CSRF攻擊)
  • ChildActionOnly(不允許使用者直接輸入Url呼叫)

       由上面描述可以看到,在AccountController中有許多的屬性標籤都和安全性息息相關,這樣一來,對於安全性的部份開發人員僅需在自己的Action加上適當的屬性標籤就能具有基本的安全性,不再像過去需要撰寫太多的程式碼在處理安全性相關的議題。

        其中要注意某些屬性標籤對於Get和Post的Http操作是有很大的限制的,舉例來說ValidateAntiforgeryToken這個屬性標籤就無法和Get使用,因此,使用ValidateAntiForgeryToken時,務必要在該Action上加上HttpPost屬性標籤,否則會發生錯誤。

        除了上述的方法概述以及屬性標籤之外,其中較為特別的是方法的內容中有個極其顯眼的類別:WebSecurity,這個類別就像是過去的MemberShip,它們的任務和功用都很單純,全都是帳戶的處理。

        介紹完表單驗證之後,我們再回到本篇文章的主題: OAuth。其實MVC的範本中就有相當豐富的OAuth範例,其中牽涉到的部份除了App_Start下的AuthConfig.cs外,再來就是Controllers中的AccountController了,在AuthConfig.cs中,主要在處理的是與第三方驗證單位的設定,觀其四個方法可以知道分別是不同的第三方單位,僅需選擇其中一個使用就可以了。
  
        點擊這個網址可以連結到Facebook的開發人員中心,但使用前需要進行一連串的註冊動作。(使用前需有Facebook帳號)

        步驟1: 輸入Facebook開發人員專屬特區的名字和名稱空間(這個可不用輸入)
image
       
         步驟2: 輸入安全驗證碼

                 image

         步驟3: 取得"應用程式ID/ API鑰匙",並設定AuthConfig.cs的參數

image
             
          而上面的"應用程式ID / API鑰匙"以及”應用程式密鑰”,記得要複製下來準備貼到App_Start底下的AuthConfig中。

image
   
        將”應用程式ID/ API鑰匙”填入appId(字串型式);將”應用程式密鑰”填入appSecret中(字串型式)。做完這堆事情之後,還得前往Facebook的應用開發中心打開WebSite With Facebook Login這項功能。

         步驟4: 啟用WebSite With Facebook Login

image
 
       點擊編輯應用程式,進入下一個面頁開啟WebSite With Facebook Login功能。

image

        萬事具備,再來就可以直接執行MVC專案並進入登入頁面。

image

        點選右側的Facebook登入按鈕。

image

image

image

總結:OAuth在MVC中相當的方便就能夠達成,可以多多參考MVC中有關註冊的範本。

2012年11月22日 星期四

ASP.Net MVC中的Route

image

        在之前的文章中曾經探討過各個MVC專案中的資料夾內容,當時對於App_Start這個資料夾僅是淺淺的帶過去,而本篇文章開始將會一個個介紹這個資料夾中的所有檔案,原因無它,因為這個資料夾中的所有檔案對於未來開發MVC專案來說都是極為重要的檔案。

        在MVC中,除去View Model Controller這幾位主角之外,另外一位舉足輕重的就是Routing。其道理很簡單,因為一個使用者的Request要如何正確且精準的被服務對於網站來說是極其重要的,總不能使用者希望看到A網頁但是網站卻回傳一個B網頁給使用者。

    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 }
            );
        }
    }
在App_Start這個資料夾中的RouteConfig.cs,其內容如上面程式所述,這是一個極短的程式碼。就讓我們一行行的解析這段程式碼所達成的功能以及其中的關竅。

      RouteConfig這個類別裡面只有一個方法:RegisterRoutes,這是一個靜態方法;亦即,使用這個方法的物件不需要特別將這個類別實體化,可以直接以 RouteConfig.RegisterRoutes呼叫它。究其方法內容,我們可以將其分成兩個部份來看,一者為規避,另一者為設定。

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

這段程式碼在描述: 任何以axd結尾並且後續任何串接路徑都不予以理會。舉個例來說:
      http://localhost/Hello.axd/world/You/
       上述這段網址正好符合了{resource}.axd/{*pathInfo}這種Url格式。其實,無論是resource或是pathInfo,這兩個其實都是一個變數而已,本身並不具有什麼特別的意義;以上述的Url來說,我們可以看成
       resource => Hello
      *pathInfo => world/You/

      pathInfo這個變數前面的*號代表著無論有多少個,所以Hello後面無論有多少個都會被視為pathInfo的一部份。再來我們要探討為什麼要忽略符合這個格式的Request呢?其實,在IIS上面除了MVC的網站專案之外,其餘還有非常多的網站也會掛在上面,因此,如果沒有特別去過濾勢必會造成這些網站的Request被Route給誤導了。

      再來就是談談Routing的設定, RegisterRoutes這個方法第二個部份就是在進行路由的設定。

routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
上面這段程式碼的功能其實很簡單,就是在比對符合{controller}/{action}/{id}這種Url格式的Request該被那個Controller的那個Action服務。舉例來說:

     http://localhost/Home/Index

    這段的解析如下:
    controller => Home
    action => Index

   大家會很好奇,那id去那了?可以翻遍了整個Url來看就是整不到可以與id對上的,難道id不存在?
   是的,就我上面所舉的那個Url來說,id確實不存在。再特別提到一點,上面所舉的Url的例子其實與 http://localhost/ 是完全等價的。其原因我們再繼續看MapRoute這個API的第三個參數:

defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }

   defaults這個參數在描述著,若Url沒有提供controller則預設會帶入"Home"作為預設值,而action的預設值則為"Index",如此一來就解釋了為什麼我說 http://localhost/Home/Index 會與 http://localhost  等價了。再來,還要再解釋id的問題。其實,在defaults這個參數中已經言明 id其實是個選擇性的(Optional),因此,id可有可沒有,這完全都不會影響到Url的判斷。

總結:Route對於MVC來說是一門很重要的必修課,因為,再好的MVC設計如果無法讓使用者的Request正確導向正確的Controller,這很容易就會造成使用者明明要的是A網頁,但是網站全傳回B網頁給他。

2012年11月21日 星期三

ASP.Net MVC中的Model

image

        打開MVC預設範例的Models資料夾可以看到內部僅有一個副檔名為CS的檔案。依照我們先前所說,MVC中的Model其實很單純,它僅將眼界放在自己的任務上,它不會去理會其它Model或是Controller,對於自己的職責範圍它總是盡力作到,而外面的世界與它全然無關。正因為這樣的特性,我們可以說Model本身是一個獨立的最小單元,它僅負責它自己該負責的部份,它不會涉入非它責任範圍的任何事項。


       在Model的工作中,我們可以簡單歸納成下列幾項:
  • 資料存取
  • 商業邏輯

      在第一項資料存取上,Model對於任何資料的存取或是擷取都是由它負責來完成,可別小看這項工作,有的時候它可能需要從遠端伺服器上下載一份文件檔或是傳上文件檔。大多的範例都是以資料庫的資料存取作為範例,不諱言,這的確也是MVC網站絕大部份時間所需要去做的事情。也因此,在網路上查詢MVC的Model時總是會在網路上找到許多有關資料庫存取的相關文章,其中較多的就是微軟努力在推行的ORM框架:Entity Framework。

    Entity Framework目前已經推行到Entity Framework 5了,這款微軟自家的ORM框架相較於由Java轉生過來的NHibernate來說是比較輕量級一點的,但是功能上當然也比NHibernate少了一些。總的來說,其實我個人還是挺看好微軟這套ORM框架的,除了它有特定的團隊在維護升級之外,它提供了許多誘人的特性,如下列:
  • Migration
  • 支援三種模式: Database First, Model First, Code First
  • Linq語法
  • Entity Framework 5在效能上的提升(有環境上的限制)
  • Data Annotation
  • Entity Relation的Fluent設定

   Entity Framework在Migration上做得很徹底,而這也是我很喜歡的一個功能,在過去開發有關資料庫的系統,如果資料表欄位需要作任何異動,對於系統來說都是一件麻煩的事情;首先,你必須要進入Database中並且進行資料表的修改,這是一件令人非常頭痛的事情。在Entity Framework上,倘若目前仍處理開發設計階段,僅需在Package Console中下幾行指令,就能很快速又簡單的修改資料表的欄位,這真的是一件利多。(我的筆電很爛,通常開了Visual Studio又要再開啟SSMS,這個時候記憶體就不太夠了)

      在前幾代的Entity Framework僅支援Database First這種特性,但是隨著版本的演進慢慢的開始支援了Model First;亦即,開發人員可以直接在Visual Studio拉模型到設計區塊中,並且設定好一些屬性就能夠快速建立及產生一個資料庫的所有資料表,這在當時的確相當驚人,因為這改變了過去系統的開發流程,從先進資料庫設定一堆資料表到了現在可以從Visual Studio中設定。但這樣仍無法滿足所有的開發人員。很多時候如果系統並不是那麼巨型,且客戶要求的時程非常的短促,這通常會將許多SA和SD的時間壓縮,會很迫切的需要快速轉入實做中,若這個時候還需要在那邊慢慢拉模型,這對於性子較急的開發人員或專案經理來說,簡直就是地獄

       除了時程問題之外,考慮另一個議題:複用性。許多時候,團隊在開發系統上會採用領域模型分析,亦即,會先將本次要開發的系統其最關鍵核心的部份建構出Domail Model(領域模型),而這個領域模型可能在下一次的專案或是延伸專案中會被使用到,這便產生了複用的需求,在過去的Entity Framework中,無論是Database First抑或是Model First都無法滿足此類要求,因為這些模式的產物很難完全移轉到其它專案身上,縱使可以移轉也需要再進行一番加工才行。為此,Code First這種模式產生了!

      Code First一如其名,這種模式就是什麼都不用管,直接看著Domail Model圖將上面的領域物件寫成一個個的類別到系統中,不需要先進資料庫也不需要打開Entity Framework專屬的設計畫面,只要很簡單的新增幾個類別就結束了。接下來我們就來實際欣賞MVC專案中Code First的真實樣貌。

     打開AccountModels.cs這個檔案,其內容如下:
 
namespace MvcApplication1.Models
{
    public class UsersContext : DbContext
    {
        public UsersContext()
            : base("DefaultConnection")
        {
        }

        public DbSet UserProfiles { get; set; }
    }

    [Table("UserProfile")]
    public class UserProfile
    {
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int UserId { get; set; }
        public string UserName { get; set; }
    }

    public class RegisterExternalLoginModel
    {
        [Required]
        [Display(Name = "使用者名稱")]
        public string UserName { get; set; }

        public string ExternalLoginData { get; set; }
    }

    public class LocalPasswordModel
    {
        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "目前密碼")]
        public string OldPassword { get; set; }

        [Required]
        [StringLength(100, ErrorMessage = "{0} 長度至少必須為 {2} 個字元。", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "新密碼")]
        public string NewPassword { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "確認新密碼")]
        [Compare("NewPassword", ErrorMessage = "新密碼與確認密碼不相符。")]
        public string ConfirmPassword { get; set; }
    }

    public class LoginModel
    {
        [Required]
        [Display(Name = "使用者名稱")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "密碼")]
        public string Password { get; set; }

        [Display(Name = "記住我?")]
        public bool RememberMe { get; set; }
    }

    public class RegisterModel
    {
        [Required]
        [Display(Name = "使用者名稱")]
        public string UserName { get; set; }

        [Required]
        [StringLength(100, ErrorMessage = "{0} 長度至少必須為 {2} 個字元。", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "密碼")]
        public string Password { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "確認密碼")]
        [Compare("Password", ErrorMessage = "密碼和確認密碼不相符。")]
        public string ConfirmPassword { get; set; }
    }

    public class ExternalLogin
    {
        public string Provider { get; set; }
        public string ProviderDisplayName { get; set; }
        public string ProviderUserId { get; set; }
    }
}
在上述程式碼中,一共有7個類別在裡面,其中第2個是在定義資料表,而最上面那一個是Entity Framework中一個很重要的類別。
    (在一般系統開發慣例來說,通常是不會把這麼多個類別塞在一個檔案中,大多時候都是一個類別一個檔案)

   這下可有趣了,這麼多個類別怎麼只有一個類別是在定義資料表,而其它類別呢?它們又是在做些什麼用途?我們先來看看前面兩個類別在做些什麼,再來討論下面那些類別。
    [Table("UserProfile")]
    public class UserProfile
    {
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int UserId { get; set; }
        public string UserName { get; set; }
    }

這個類別所定義的資料表很簡單,它僅有兩個欄位: UserId以及UserName。其中比較特別的部份是UserId的上頭有兩個標籤屬性,一者為[Key]而另一個則看起來複雜的樣子。其實望文生義可以很快速的瞭解到,這兩個標籤在說明UserId對於這張表來說是主索引鍵,而第二個標籤在說明它的值是自動產生的,而產生出來的值會依照其屬性的資料型別int作值的遞增;也就是說,第一筆資料的UserId是0而第二筆是1如此類推之。

      類別上的標籤屬性就更加讓人容易明白,它就很直白的說明這是一張資料表,而其資料表的名稱為UserProfile(註:資料表的名稱可以和類別名稱不一樣)。這些標籤屬性其實有個專有名詞: Data Annotation
    public class UsersContext : DbContext
    {
        public UsersContext()
            : base("DefaultConnection")
        {
        }

        public DbSet UserProfiles { get; set; }
    }

任何一個ORM的框架都勢必會有一個本文(Context),大家可以把它當作成一個容器,一個系統可以有多個Context但是這些Context彼此互相獨立,其目的在於確保資料的一致性和可追蹤的特性。上面這個類別繼承了DbContext,一旦它繼承了這個類別它就具有Code First的Context所有應有的能力。在上述程式碼中可以看到這個類別中有一個屬性:

      public DbSet<UserProfile> UserProfile {get; set;}

     可以將其解讀成這個資料庫中有一張資料表:UserProfiles。而建構式中傳遞給父類別建構子的字串其實我們可以在Web.config(註:不是Views資料夾中的Web.config)中找到:

 <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-MvcApplication1-20121118193713;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-MvcApplication1-20121118193713.mdf" providerName="System.Data.SqlClient" />
  </connectionStrings>
 
   意思就是說明這個Context的資料庫連線字串為Web.config中所定義的。

    話題回到之前所討論的問題:其它類別是在做什麼的?
    其實,Model在系統開發上有好幾種,像是這種負責資料庫存取的Model我們會稱其為Data Access Object(資料存取物件),而為了讓View能夠輕鬆用來呈現畫面的就稱之為View Model(視圖物件),若僅是單傳用來在函式之間傳遞的則為Data Transfer Object(資料傳遞物件)。在AccountModels.cs檔案中的其它類別: RegisterExternalLoginModel, LocalPasswordModel, LoginModel, RegisterModel, ExternalLogin都屬於View Model。

總結:本章大略講解了AccountModels.cs這個檔案中的內容,但並非深入到Model的精髓,之後的文章會再深入到Model的開發技巧包括常用的設計樣式: 倉儲樣式, 單一工作樣式。