[ASP.NET][MVC] ASP.NET MVC (12) : 設計自己的 View 輸出-以 CSV 為例

我們在 ASP.NET MVC (7) 中曾介紹了 ASP.NET MVC 的 View 內建了 9 種不同的 View,基本上這些 View 均足以應付大部份的資料呈現需求,但是它也不是不能擴充的,像是一些特殊的 View (例如圖表或特殊檔案或特別的資料格式),就需要由開發人員自己設計,不過 MVC 的 View 設計上也不難,只要將自己的 View 加入 HTTP 處理流程中即可。

我們在 ASP.NET MVC (7) 中曾介紹了 ASP.NET MVC 的 View 內建了 9 種不同的 View,基本上這些 View 均足以應付大部份的資料呈現需求,但是它也不是不能擴充的,像是一些特殊的 View (例如圖表或特殊檔案或特別的資料格式),就需要由開發人員自己設計,不過 MVC 的 View 設計上也不難,只要將自己的 View 加入 HTTP 處理流程中即可。

我們在 Controller 中的每一個動作,最後都會回傳一個 View,而預設的型別是 ActionResult,ActionResult 是 View 的基底類別,MVC 內建的 9 種 View 都衍生自 ActionResult,也就是說,我們可以透過繼承這個抽象類別,來取得產生自己需要的 View 的基本功能,而我們只需要覆寫 ActionResult.ExecuteResult() 方法即可。

所以我們在專案中新增一個類別,命名為 CsvResult,並設定它繼承 ActionResult 類別 (Visual Studio 會偵測是否已實作必要成員,將會以一個小的底線符號通知)。而本例是使用泛型的方式產生 CSV,因此可直接使用下列程式碼取代:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Reflection;
   5: using System.Text;
   6: using System.Web;
   7: using System.Web.Mvc;
   8:  
   9: namespace MvcApplication1
  10: {
  11:     public class CsvResult<TModel, T> : ActionResult where TModel : IEnumerable<T>
  12:     {
  13:         public string FileName { get; set; }
  14:         public TModel Model { get; set; }
  15:  
  16:         public override void ExecuteResult(ControllerContext context)
  17:         {
  18:             StringBuilder csvBuilder = new StringBuilder();
  19:  
  20:             context.HttpContext.Response.Clear();
  21:  
  22:             context.HttpContext.Response.Buffer = true;
  23:             context.HttpContext.Response.ContentType = "text/csv";
  24:             context.HttpContext.Response.AddHeader("content-disposition", "attachment; filename=" + this.FileName);
  25:  
  26:             PropertyInfo[] modelProperties = typeof(T).GetProperties();
  27:             StringBuilder itemBuilder = new StringBuilder();
  28:  
  29:             // render header.
  30:             foreach (PropertyInfo modelProperty in modelProperties)
  31:             {
  32:                 if (modelProperty.DeclaringType == typeof(T))
  33:                 {
  34:                     if (itemBuilder.Length == 0)
  35:                         itemBuilder.Append(modelProperty.Name);
  36:                     else
  37:                         itemBuilder.Append(",").Append(modelProperty.Name);
  38:                 }
  39:             }
  40:  
  41:             csvBuilder.Append(itemBuilder);
  42:  
  43:             // render body.
  44:             foreach (T item in this.Model)
  45:             {
  46:                 csvBuilder.Append(Environment.NewLine);
  47:                 itemBuilder.Clear();
  48:  
  49:                 foreach (PropertyInfo modelProperty in modelProperties)
  50:                 {
  51:                     if (modelProperty.DeclaringType == typeof(T))
  52:                     {
  53:                         if (itemBuilder.Length == 0)
  54:                             itemBuilder.Append(modelProperty.GetValue(item, null).ToString());
  55:                         else
  56:                             itemBuilder.Append(",").Append(modelProperty.GetValue(item, null).ToString());
  57:                     }
  58:                 }
  59:  
  60:                 csvBuilder.Append(itemBuilder);
  61:             }
  62:  
  63:             context.HttpContext.Response.Write(csvBuilder.ToString());
  64:         }
  65:     }
  66: }

 

完成後,在 MyController 中,加入一個 CustomerCsv 的方法:

   1: public ActionResult CustomerCsv()
   2: {
   3:     var model = Models.CustomerDataContext.LoadCustomers();
   4:     return new CsvResult<IEnumerable<Models.Customer>, Models.Customer>() { FileName = "Customers.csv", Model = model };
   5: }

 

然後啟動程式,打開瀏覽器,瀏覽 http://[root]/My/CustomerCsv,你會發現檔案已自動下載到瀏覽器的下載資料夾內:

image

打開來看,Northwind 的 Customers 的資料都會在裡面,不過會只有宣告在 Model.Customer 的屬性的資料才會出現。

Reference:

http://www.dotnetcurry.com/ShowArticle.aspx?ID=484