2013年6月9日 星期日

[Asp.net MVC] Custom Action Filter and Trace

上一篇,已經針對 Action Filter 作了一個初步的介紹,這篇就來試著實作自己的Action Filter。

Action Filter 是實作 一個繼承 ActionFilterAttribute 的一個 Attribute 類別, ActionFilterAttribute 是一個抽象類別,包含四個 virtual methods : OnActionExecuting、OnActionExecuted、OnResultExecuting、OnResultExecuted。

這邊是MSDN的原文說明,相信會讓大家有更清楚的認知:

An action filter is implemented as an attribute class that inherits from ActionFilterAttribute. ActionFilterAttribute is an abstract class that has four virtual methods that you can override: OnActionExecuting, OnActionExecuted, OnResultExecuting, and OnResultExecuted. To implement an action filter, you must override at least one of these methods.

The ASP.NET MVC framework will call the OnActionExecuting method of your action filter before it calls any action method that is marked with your action filter attribute. Similarly, the framework will call the OnActionExecuted method after the action method has finished.

The OnResultExecuting method is called just before the ActionResult instance that is returned by your action is invoked. The OnResultExecuted method is called just after the result is executed. These methods are useful for performing actions such as logging, output caching, and so forth.

 

首先我們先建立一個 MVC 專案,並新增一個Filter資料夾,然後在其中新增一個繼承 ActionFilterAttribute 的 Trace class ,並 override ActionFilterAttribute 的四個 virtual methods :

    public class Trace : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Debug.WriteLine("OnActionExecuting");
base.OnActionExecuting(filterContext);
}

public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Debug.WriteLine("OnActionExecuted");
base.OnActionExecuted(filterContext);
}

public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Debug.WriteLine("OnResultExecuting");
base.OnResultExecuting(filterContext);
}

public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Debug.WriteLine("OnResultExecuted");
base.OnResultExecuted(filterContext);
}
}

 


接著,按下F5讓它動起來,觀察執行的先後順序是否如同上面所描述的一樣  :


sdsd


 


那如果 Action Filter 套用多個呢?  首先我們再新增一個 ActionFilter (Log) :

    public class Trace : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Debug.WriteLine("OnActionExecuting from Trace");
base.OnActionExecuting(filterContext);
}

public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Debug.WriteLine("OnActionExecuted from Trace");
base.OnActionExecuted(filterContext);
}

public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Debug.WriteLine("OnResultExecuting from Trace");
base.OnResultExecuting(filterContext);
}

public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Debug.WriteLine("OnResultExecuted from Trace");
base.OnResultExecuted(filterContext);
}
}

public class Log : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Debug.WriteLine("OnActionExecuting from Log");
base.OnActionExecuting(filterContext);
}

public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Debug.WriteLine("OnActionExecuted from Log");
base.OnActionExecuted(filterContext);
}

public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Debug.WriteLine("OnResultExecuting from Log");
base.OnResultExecuting(filterContext);
}

public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Debug.WriteLine("OnResultExecuted from Log");
base.OnResultExecuted(filterContext);
}
}

將 Trace、Log 兩個 Filter 都套用到 Action之後,我們會得到以下的結果


sdx


從上面的結果我們可以發現 Action Filter 中的 Executing 是由上而下執行,而 Executed 是由下而上執行,類似堆疊 "後進先出"的概念。


ActionExecuting、ResultExcuting 執行階段是可以被終止、Cancel 的(例如當發生execption時),所以我們再把 Trace 這個 Filter 修改一下,使其拋出一個 non-null Result,來模擬這個狀況

        public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Debug.WriteLine("OnActionExecuting from Trace");
//加入下面這一行,讓result 為 non-null
//也就是 filterContext.RouteData.Values.ContainsValue("Cancel") = true
filterContext.Result = new EmptyResult();
base.OnActionExecuting(filterContext);
}

 


我們會得到以下的結果 :


sss


可以發現,當Trace 的 OnActionExecuting 事件被中止,尚未執行的Log OnActionExecuting 也一樣被中止,直接進入OnResultExecuting 繼續執行,由此可知 OnActionExecuting 和 OnResultExecuting 兩個是獨立的流程,並不會因為 OnActionExecuting 發生Cancel 而影響 OnResultExecuting 。


 


參考資料 :


MSDN -- Filtering in ASP.NET MVC

2013年6月8日 星期六

[MS-SQL] 備份組包含目前現有的XXX資料庫以外的資料庫備份

當我們在還原資料庫時,時常會遇到這個錯誤,這邊就記錄一下完整步驟,避免每次都要重新請教G大神或從已經爆炸的書籤中找解法 Orz....
錯誤畫面 :
未命名

解決方式 :
1.先指向   資料庫---> 系統資料庫 –> master   , 新增sql查詢。
masterDB
2.在sql 查詢視窗 輸入以下指令 :
restore DATABASE xxxx  -- 指定 欲還原的資料庫名稱db  
from disk='D:\DbBack\xxxx.bak'  -- 設定 資料庫備份檔 .bak 路徑
with 
move 'xxxx' to 'C:\Program Files\Microsoft SQL Server\MSSQL11.SQLEXPRESS\MSSQL\Data\xxxx.mdf',      -- 設定成 MSSQL資料庫存放路徑 (mdf)
move 'xxxx_Log' to 'C:\Program Files\Microsoft SQL Server\MSSQL11.SQLEXPRESS\MSSQL\Data\xxxx_log.ldf'  -- 設定Log 存放路徑
,NoRecovery
,Replace
go

3.執行完畢,重新整理資料庫畫面,會變成以下畫面 :

正在還原

4.接著再對 (正在還原....) 的這個資料庫點右鍵    工作-->還原

還源

5.加入備份檔 .bak 所在路徑

還原2

6.勾選還原資料庫

勾選還原資料庫



OK!! 很懶惰的一篇分享文.....(用一堆圖片帶過 Orz)  ….. 結束.....The End

2013年6月6日 星期四

[Asp.net MVC] Action Filter


在ASP.net MVC 中有以下幾種預設 filters providers :
過濾器類型 中文名稱 介面 實作
Authorization Filters 授權過濾器 IAuthorizationFilter AuthorizeAttribute
ChildActionOnlyAttribute
RequireHttpsAttribute
ValidateAntiForgeryTokenAttribute
ValidateInputAttribute
Action Filters 動作過濾器 IActionFilter ActionFilterAttribute
OutputCacheAttribute
AsyncTimeoutAttribute
Result Filters 結果過濾器 IResultFilter ActionFilterAttribute
Exception Filters 例外過濾器 IExceptionFilter HandleErrorAttribute
OutputCacheAttribute

執行順序 :
action filter life cycle
                                                       (圖片引用自http://www.dotnetexpertguide.com)


原文說明 :
  • Authorization filter, which makes security decisions about whether to execute an action method, such as performing authentication or validating properties of the request.
  • Action filter, which wraps the action method execution. This filter can perform additional processing, such as providing extra data to the action method, inspecting the return value, or canceling execution of the action method
  • Result filter, which wraps execution of the ActionResult object. This filter can perform additional processing of the result, such as modifying the HTTP response.
  • Exception filter, which executes if there is an unhandled exception thrown somewhere in action method, starting with the authorization filters and ending with the execution of the result. Exception filters can be used for tasks such as logging or displaying an error page.

ASP.NET MVC 目前內建的幾種 Filter :
  • [Authorize] 與Membership or FormsAuthentication 配合使用,驗證使用者是否有被授權執行Action,若驗證失敗會轉向預設登入頁面。
  • [AllowAnonymous] 套用此屬用,將會使此Action不被 Authorize 所作用,可不接受驗證授權即執行Action。
  • [ChildActionOnly] 套用此屬性之後,該Action 將只可透過 Html.RenderAction 執行,不可由外部URL 或是 RedirectToAction 所執行。
  • [RequireHttps] 僅可透過Https安全連線存取,若透過一般Http做存取,將會自動導向Https。
  • [ValidateInput] ASP.NET MVC 預設會將所有透過Form來的輸入資料作驗證,檢查是否有惡意的標籤或是程式碼,因此若要取得使用者所輸入的HTML標籤或是JS,就必須將此屬性設定為False。
  • [ValidateAntiForgeryToken] 在ASP.NET MVC 4 所新增的屬性,用以防止CSRF攻擊,使用時除了必須在Action套用此屬性之外,在View頁面的 Form表單中,必須在Sumbit之前插入 @Html.AntiForgeryTolen( ),否則會引發 『需要的反仿冒 Cookie "__RequestVerificationToken" 不存在 』的錯誤。
  • [AsyncTimeOut] 設定執行非同步控制時的 TimeOut 時間(毫秒),這邊須注意此屬性僅在 Controller 繼承 AsyncController 時,才有作用。
  • [NoAsyncTimeOut] 設定執行非同步控制時,不會有TimeOut時間,會不斷等待直到執行完畢為止,也一樣必須繼承AsyncController時,才有作用。
  • [OutputCache] 套用此屬性,可設定瀏覽器快取的相關設定。
  • [HandleError] 套用此屬性之後,不管是在Action執行階段,或者Result時發生錯誤,皆會觸發HandleError.OnException,若沒特別指定錯誤事件和指定的View,預設會將頁面導向預設的 /View/Shared/Error.cshtml。

Filter 作用域 :
1.僅套用於單一Action
    
    [HandleError]  //加在Action 之上
    public ActionResult Contact()
    {
        ViewBag.Message = "Your contact page.";
        return View();
    }

2.套用整個Controller
    
    [HandleError] //套用整個Controller
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
            return View();
        }
    }

3.註冊為全域(套用整個Asp.net MVC 專案)
    
//在App_Start中的FilterConfig中註冊filter
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }
    }





參考資訊

msdn--Filtering in ASP.NET MVC

asp.net mvc 官網教學 Home/MVC/Tutorials/Chapter 11. Hands On Labs/ASP.NET MVC 4 Custom Action Filters

2013年6月3日 星期一

[Asp.net MVC] FileResult

在ASP.NET MVC 中,若要實現檔案文件等的上下傳,最簡單的就是使用FileResult。

FileResult 是基於檔案文件的一個ActionResult實作,目前在ASP.NET MVC 中定義了如下三個FileResult的實作 :

  • FileContentResult: Sends the contents of a binary file to the response.
protected virtual FileContentResult File(byte[] fileContents, string contentType, string fileDownloadName);
  • FilePathResult: Sends the contents of a file to the response
protected virtual FilePathResult File(string fileName, string contentType, string fileDownloadName);
  • FileStreamResult: Sends binary content to the response by using a Stream instance
protected virtual FileStreamResult File(Stream fileStream, string contentType, string fileDownloadName);

實作範例 

1.FileContentResult

        public FileContentResult fileContextResult(int productId = 2)
        {
            var prod = db.Products.FirstOrDefault(q => q.ProductID == productId);
            return prod != null ? File(prod.ImageData, prod.ImageMimeType) : null;
        }
2.FilePathResult
        public FilePathResult filePathResult()
        {
            ViewBag.Status = "FilePathResult";
            string path = Server.MapPath("/Content/Guns/2.png");
            return File(path, "image/png", "02.png");
        }
3.FileStreamResult
        public FileStreamResult fileStreamResult()
        {
            string path = Server.MapPath("/Content/Guns/2.png");
            var image = new Bitmap(path);            
            MemoryStream  stream = new MemoryStream();
            image.Save(stream, ImageFormat.Jpeg);
            stream.Seek(0, SeekOrigin.Begin);
            return File(stream, "image/jpeg");
        }

View

@ViewBag.Status

@Html.ActionLink("Download", "filePathResult")

上傳實作

Action :
        [HttpPost]
        public ActionResult Edit(Product product, HttpPostedFileBase image)
        {
        // 注意這邊須使用 HttpPostedFileBase 來接 multipart/form-data 格式的資料
            if (ModelState.IsValid)
            {
                if (image != null)
                {
                    product.ImageMimeType = image.ContentType;
                    product.ImageData = new byte[image.ContentLength];
                    image.InputStream.Read(product.ImageData, 0, image.ContentLength);
                }

                db.SaveProduct(product);
                TempData["message"] = string.Format("{0} has been saved", product.Name);
                return RedirectToAction("Index");
            }
            return View(product);
        }

View :
@using (Html.BeginForm("Edit", "Admin", FormMethod.Post, new { enctype= "multipart/form-data" }))
{
    
    @Html.EditorForModel();
    
    
Image
@if (Model.ImageData == null) { placehold } else { }
Upload new File
@Html.ActionLink("Cancel", "Index") }

這邊的重點在於
Html.BeginForm("Edit", "Admin", FormMethod.Post, new { enctype= "multipart/form-data" })

必須先將 Form 宣告成 multipart,代表此「表單」包含多重欄位。詳細說明請參閱RFC 2388 。

PS:另外若非不得已請儘量避免在View中出現商業邏輯,如上方的  if   else ,這邊僅是方便文章作呈現。

參考資訊
蒋金楠(Artech)的博客
RFC 2388
mime-types-full
石頭閒語 --HTML 5 -File API 簡易教學