摘要:在这个Tip中,Stephen Walther创建了一个自定义的ActionResult,可以由ASP.NET MVC控制器action返回。该ActionResult从一个LINQ to SQL查询生成了一个Excel文档。
译注:从本篇开始,为了方便,仅保留了C#代码。对VB.NET感兴趣的朋友可以参见原文。
在MVC应用程序中,控制器action可以返回一个ActionResult。特别是,他能够返回一些从ActionResult基类继承的东西——
-
- ViewResult
-
- EmptyResult
-
- RedirectResult
-
- RedirectToRouteResult
-
- JsonResult
-
- ContentResult
例如,你可以使用ViewResult向浏览器返回一个特定的视图,使用ContentResult向浏览器返回文本内容。
但是,如果你想向浏览器返回其他类型的内容——如图片、PDF文件或Excel文档呢?在这些情况下,你可以创建自己的ActionResult。在这个Tip中,我会想你展示如何创建一个能返回Excel文档的ActionResult。
清单1包含了ExcelResult的代码。
清单1 - ExcelResult.cs
2using System.Web.Mvc;
3using System.Data.Linq;
4using System.Collections;
5using System.IO;
6using System.Web.UI.WebControls;
7using System.Linq;
8using System.Web;
9using System.Web.UI;
10using System.Drawing;
11
12
13namespace Tip2
14{
15 public class ExcelResult : ActionResult
16 {
17 private DataContext _dataContext;
18 private string _fileName;
19 private IQueryable _rows;
20 private string[] _headers = null;
21
22 private TableStyle _tableStyle;
23 private TableItemStyle _headerStyle;
24 private TableItemStyle _itemStyle;
25
26 public string FileName
27 {
28 get { return _fileName; }
29 }
30
31 public IQueryable Rows
32 {
33 get { return _rows; }
34 }
35
36
37 public ExcelResult(DataContext dataContext, IQueryable rows, string fileName)
38 :this(dataContext, rows, fileName, null, null, null, null)
39 {
40 }
41
42 public ExcelResult(DataContext dataContext, string fileName, IQueryable rows, string[] headers)
43 : this(dataContext, rows, fileName, headers, null, null, null)
44 {
45 }
46
47 public ExcelResult(DataContext dataContext, IQueryable rows, string fileName, string[] headers, TableStyle tableStyle, TableItemStyle headerStyle, TableItemStyle itemStyle)
48 {
49 _dataContext = dataContext;
50 _rows = rows;
51 _fileName = fileName;
52 _headers = headers;
53 _tableStyle = tableStyle;
54 _headerStyle = headerStyle;
55 _itemStyle = itemStyle;
56
57 // provide defaults
58 if (_tableStyle == null)
59 {
60 _tableStyle = new TableStyle();
61 _tableStyle.BorderStyle = BorderStyle.Solid;
62 _tableStyle.BorderColor = Color.Black;
63 _tableStyle.BorderWidth = Unit.Parse("2px");
64 }
65 if (_headerStyle == null)
66 {
67 _headerStyle = new TableItemStyle();
68 _headerStyle.BackColor = Color.LightGray;
69 }
70 }
71
72 public override void ExecuteResult(ControllerContext context)
73 {
74 // Create HtmlTextWriter
75 StringWriter sw = new StringWriter();
76 HtmlTextWriter tw = new HtmlTextWriter(sw);
77
78 // Build HTML Table from Items
79 if (_tableStyle != null)
80 _tableStyle.AddAttributesToRender(tw);
81 tw.RenderBeginTag(HtmlTextWriterTag.Table);
82
83 // Generate headers from table
84 if (_headers == null)
85 {
86 _headers = _dataContext.Mapping.GetMetaType(_rows.ElementType).PersistentDataMembers.Select(m => m.Name).ToArray();
87 }
88
89
90 // Create Header Row
91 tw.RenderBeginTag(HtmlTextWriterTag.Thead);
92 foreach (String header in _headers)
93 {
94 if (_headerStyle != null)
95 _headerStyle.AddAttributesToRender(tw);
96 tw.RenderBeginTag(HtmlTextWriterTag.Th);
97 tw.Write(header);
98 tw.RenderEndTag();
99 }
100 tw.RenderEndTag();
101
102
103
104 // Create Data Rows
105 tw.RenderBeginTag(HtmlTextWriterTag.Tbody);
106 foreach (Object row in _rows)
107 {
108 tw.RenderBeginTag(HtmlTextWriterTag.Tr);
109 foreach (string header in _headers)
110 {
111 string strValue = row.GetType().GetProperty(header).GetValue(row, null).ToString();
112 strValue = ReplaceSpecialCharacters(strValue);
113 if (_itemStyle != null)
114 _itemStyle.AddAttributesToRender(tw);
115 tw.RenderBeginTag(HtmlTextWriterTag.Td);
116 tw.Write( HttpUtility.HtmlEncode(strValue));
117 tw.RenderEndTag();
118 }
119 tw.RenderEndTag();
120 }
121 tw.RenderEndTag(); // tbody
122
123 tw.RenderEndTag(); // table
124 WriteFile(_fileName, "application/ms-excel", sw.ToString());
125 }
126
127
128 private static string ReplaceSpecialCharacters(string value)
129 {
130 value = value.Replace("’", "'");
131 value = value.Replace("“", "\"");
132 value = value.Replace("”", "\"");
133 value = value.Replace("–", "-");
134 value = value.Replace("…", "");
135 return value;
136 }
137
138 private static void WriteFile(string fileName, string contentType, string content)
139 {
140 HttpContext context = HttpContext.Current;
141 context.Response.Clear();
142 context.Response.AddHeader("content-disposition", "attachment;filename=" + fileName);
143 context.Response.Charset = "";
144 context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
145 context.Response.ContentType = contentType;
146 context.Response.Write(content);
147 context.Response.End();
148 }
149 }
150}
所有的ActionResult都必须直接或间接继承自ActionResult基类。清单1中的ExcelResult就是这样,实际上,它直接继承了ActionResult类。ActionResult基类中有一个方法是必须实现的——Execute()方法。调用Execute()方法会生成ActionResult的结果产生的内容。
在清单1中,Execute()方法用于从Linq to SQL查询生成Excel文档。Execute()方法会调用WriteFile()方法将生成的Excel文档以正确的MIME类型写入到浏览器中。
通常,你不会从控制器action中直接返回一个ActionResult,而是利用Controller类提供的某个方法——
-
- View()
-
- Redirect()
-
- RedirectToAction()
-
- RedirectToRoute()
-
- Json()
-
- Content()
例如,如果你想从一个控制器action中返回一个视图,不要直接返回一个ViewResult,而是调用View()方法。View()方法会实例化一个ViewResult并将这个新的ViewResult返回给浏览器。
清单2中的代码包含三个应用于Controller类的扩展方法。这些扩展方法向Controller类添加了一个名为Excel()的方法。Excel()方法会返回一个ExcelResult。
清单2 - ExcelControllerExtensions.cs (C#)
2using System.Web.Mvc;
3using System.Data.Linq;
4using System.Collections;
5using System.Web.UI.WebControls;
6using System.Linq;
7
8namespace Tip2
9{
10 public static class ExcelControllerExtensions
11