[ZT] [绝对不是我写的]ASP.NET MVC Tip #2
2010-03-25 09:56
323 查看
[翻译] ASP.NET MVC Tip #2 - 创建可以返回Excel文档的自定义ActionResult
原文地址:http://stephenwalther.com/blog/archive/2008/06/16/asp-net-mvc-tip-2-create-a-custom-action-result-that-returns-microsoft-excel-documents.aspx
摘要:在这个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
所有的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#)
清单3中的控制器展示了如何在控制器中使用Excel()扩展方法。该控制器包含三个方法,名字分别是GenerateExcel1()、GenerateExcel2()和GenerateExcel3()。所有这三个控制器action都返回Excel文档,这些Excel是从Movies数据表生成的。
清单3 - HomeController.cs (C#)
最后,清单4中的Index.aspx视图展示了如何调用GenerateExcel()控制器action来生成Excel文档。注意其中的三个链接使用了GenerateExcel的三个版本。
清单4 - Index.aspx
打开Index视图,可以看到如图1所示的页面
图1 - Index.aspx视图
当单击其中一个Generate Excel链接后,你可以得到不同的Excel文档。例如,单击第一个链接之后,你会得到如图2所示的Excel文档。
图2 - Data.xls
一点小瑕疵。单击了某个链接生成Excel文档后,你会看到一个如图3所示的警告。不幸的是,没有什么办法绕过这个警告(关于该警告的更多信息,请参见:http://blogs.msdn.com/vsofficedeveloper/pages/Excel-2007-Extension-Warning.aspx)。
图3 - 来自Microsoft Internet Explorer的警告
仿照该Tip介绍的方法,你可以创建各种类型的ActionResult。例如,你可以创建Image ActionResult、Word ActionResult或者PDF ActionResult。
此处下载源代码:http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip2/Tip2.zip。
原文地址:http://stephenwalther.com/blog/archive/2008/06/16/asp-net-mvc-tip-2-create-a-custom-action-result-that-returns-microsoft-excel-documents.aspx
摘要:在这个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
using System; using System.Web.Mvc; using System.Data.Linq; using System.Collections; using System.IO; using System.Web.UI.WebControls; using System.Linq; using System.Web; using System.Web.UI; using System.Drawing; namespace Tip2 { public class ExcelResult : ActionResult { private DataContext _dataContext; private string _fileName; private IQueryable _rows; private string[] _headers = null; private TableStyle _tableStyle; private TableItemStyle _headerStyle; private TableItemStyle _itemStyle; public string FileName { get { return _fileName; } } public IQueryable Rows { get { return _rows; } } public ExcelResult(DataContext dataContext, IQueryable rows, string fileName) :this(dataContext, rows, fileName, null, null, null, null) { } public ExcelResult(DataContext dataContext, string fileName, IQueryable rows, string[] headers) : this(dataContext, rows, fileName, headers, null, null, null) { } public ExcelResult(DataContext dataContext, IQueryable rows, string fileName, string[] headers, TableStyle tableStyle, TableItemStyle headerStyle, TableItemStyle itemStyle) { _dataContext = dataContext; _rows = rows; _fileName = fileName; _headers = headers; _tableStyle = tableStyle; _headerStyle = headerStyle; _itemStyle = itemStyle; // provide defaults if (_tableStyle == null) { _tableStyle = new TableStyle(); _tableStyle.BorderStyle = BorderStyle.Solid; _tableStyle.BorderColor = Color.Black; _tableStyle.BorderWidth = Unit.Parse("2px"); } if (_headerStyle == null) { _headerStyle = new TableItemStyle(); _headerStyle.BackColor = Color.LightGray; } } public override void ExecuteResult(ControllerContext context) { // Create HtmlTextWriter StringWriter sw = new StringWriter(); HtmlTextWriter tw = new HtmlTextWriter(sw); // Build HTML Table from Items if (_tableStyle != null) _tableStyle.AddAttributesToRender(tw); tw.RenderBeginTag(HtmlTextWriterTag.Table); // Generate headers from table if (_headers == null) { _headers = _dataContext.Mapping.GetMetaType(_rows.ElementType).PersistentDataMembers.Select(m => m.Name).ToArray(); } // Create Header Row tw.RenderBeginTag(HtmlTextWriterTag.Thead); foreach (String header in _headers) { if (_headerStyle != null) _headerStyle.AddAttributesToRender(tw); tw.RenderBeginTag(HtmlTextWriterTag.Th); tw.Write(header); tw.RenderEndTag(); } tw.RenderEndTag(); // Create Data Rows tw.RenderBeginTag(HtmlTextWriterTag.Tbody); foreach (Object row in _rows) { tw.RenderBeginTag(HtmlTextWriterTag.Tr); foreach (string header in _headers) { string strValue = row.GetType().GetProperty(header).GetValue(row, null).ToString(); strValue = ReplaceSpecialCharacters(strValue); if (_itemStyle != null) _itemStyle.AddAttributesToRender(tw); tw.RenderBeginTag(HtmlTextWriterTag.Td); tw.Write( HttpUtility.HtmlEncode(strValue)); tw.RenderEndTag(); } tw.RenderEndTag(); } tw.RenderEndTag(); // tbody tw.RenderEndTag(); // table WriteFile(_fileName, "application/ms-excel", sw.ToString()); } private static string ReplaceSpecialCharacters(string value) { value = value.Replace("’", "'"); value = value.Replace("“", "\""); value = value.Replace("”", "\""); value = value.Replace("–", "-"); value = value.Replace("…", "..."); return value; } private static void WriteFile(string fileName, string contentType, string content) { HttpContext context = HttpContext.Current; context.Response.Clear(); context.Response.AddHeader("content-disposition", "attachment;filename=" + fileName); context.Response.Charset = ""; context.Response.Cache.SetCacheability(HttpCacheability.NoCache); context.Response.ContentType = contentType; context.Response.Write(content); context.Response.End(); } } }
所有的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#)
using System; using System.Web.Mvc; using System.Data.Linq; using System.Collections; using System.Web.UI.WebControls; using System.Linq; namespace Tip2 { public static class ExcelControllerExtensions { public static ActionResult Excel ( this Controller controller, DataContext dataContext, IQueryable rows, string fileName ) { return new ExcelResult(dataContext, rows, fileName, null, null, null, null); } public static ActionResult Excel ( this Controller controller, DataContext dataContext, IQueryable rows, string fileName, string[] headers ) { return new ExcelResult(dataContext, rows, fileName, headers, null, null, null); } public static ActionResult Excel ( this Controller controller, DataContext dataContext, IQueryable rows, string fileName, string[] headers, TableStyle tableStyle, TableItemStyle headerStyle, TableItemStyle itemStyle ) { return new ExcelResult(dataContext, rows, fileName, headers, tableStyle, headerStyle, itemStyle); } } }
清单3中的控制器展示了如何在控制器中使用Excel()扩展方法。该控制器包含三个方法,名字分别是GenerateExcel1()、GenerateExcel2()和GenerateExcel3()。所有这三个控制器action都返回Excel文档,这些Excel是从Movies数据表生成的。
清单3 - HomeController.cs (C#)
using System; using System.Collections.Generic; using System.Linq; using System.Data.Linq; using System.Data.Linq.Mapping; using System.Web.UI.WebControls; using System.Web; using System.Web.Mvc; using Tip2.Models; using Tip2; namespace Tip2.Controllers { public class HomeController : Controller { private MovieDataContext db = new MovieDataContext(); public ActionResult Index() { return View(); } /// <summary> /// Generates Excel document using headers grabbed from property names /// </summary> public ActionResult GenerateExcel1() { return this.Excel(db, db.Movies, "data.xls"); } /// <summary> /// Generates Excel document using supplied headers /// </summary> public ActionResult GenerateExcel2() { var rows = from m in db.Movies select new {Title=m.Title, Director=m.Director}; return this.Excel(db, rows, "data.xls", new[] { "Title", "Director" }); } /// <summary> /// Generates Excel document using supplied headers and using supplied styles /// </summary> public ActionResult GenerateExcel3() { var rows = from m in db.Movies select new { Title = m.Title, Director = m.Director }; var headerStyle = new TableItemStyle(); headerStyle.BackColor = System.Drawing.Color.Orange; return this.Excel(db, rows, "data.xls", new[] { "Title", "Director" }, null, headerStyle, null); } } }
最后,清单4中的Index.aspx视图展示了如何调用GenerateExcel()控制器action来生成Excel文档。注意其中的三个链接使用了GenerateExcel的三个版本。
清单4 - Index.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="Tip2.Views.Home.Index" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Index Page</title> <style type="text/css"> li { margin-bottom: 5px; } </style> </head> <body> <div> <h1>Generate Microsoft Excel Document</h1> <ul> <li> <a href="/Home/GenerateExcel1">Generate</a> - Generates an Excel document by using the entity property names for column headings and the default formatting. </li> <li> <a href="/Home/GenerateExcel2">Generate</a> - Generates an Excel document by using supplied header names and default formatting. </li> <li> <a href="/Home/GenerateExcel3">Generate</a> - Generates an Excel document by using supplied header names and supplied formatting. </li> </ul> </div> </body> </html>
打开Index视图,可以看到如图1所示的页面
图1 - Index.aspx视图
当单击其中一个Generate Excel链接后,你可以得到不同的Excel文档。例如,单击第一个链接之后,你会得到如图2所示的Excel文档。
图2 - Data.xls
一点小瑕疵。单击了某个链接生成Excel文档后,你会看到一个如图3所示的警告。不幸的是,没有什么办法绕过这个警告(关于该警告的更多信息,请参见:http://blogs.msdn.com/vsofficedeveloper/pages/Excel-2007-Extension-Warning.aspx)。
图3 - 来自Microsoft Internet Explorer的警告
仿照该Tip介绍的方法,你可以创建各种类型的ActionResult。例如,你可以创建Image ActionResult、Word ActionResult或者PDF ActionResult。
此处下载源代码:http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip2/Tip2.zip。
相关文章推荐
- [翻译] ASP.NET MVC Tip #2 - 创建可以返回Excel文档的自定义ActionResult
- 实战ASP.NET MVC 1.0 #2,主细表(Master-Detail)的Detail部份
- ASP.NET MVC 下UpdateModel可空未填写的参数为Null,为何不是空字符串
- [翻译] ASP.NET MVC Tip #4 - 创建自定义数据控制器基类
- Asp.Net MVC中Html.TextBox扩展,其行为也许不是你期望的
- 用网站(WebSite而不是WebProject)项目构建ASP.NET MVC网站
- asp.net MVC中的tip
- ASP.NET MVC 换肤方案(ZT)
- ASP.NET MVC Tip #18 – 参数化 HTTP Context
- [翻译] ASP.NET MVC Tip #13 – 对自定义路由进行单元测试
- ASP.NET MVC——螺旋进步的产物(zt)
- asp.net web form 使用URL路由 注不是mvc中的路由
- asp.net mvc 身份验证中返回绝对路径的ReturnUrl
- ASP.NET MVC - Tip: System.Web.Mvc.dll引用
- ASP.NET MVC Tip #15 – 传递浏览器Cookies和服务器变量作为Action参数
- [翻译] ASP.NET MVC Tip #10 - 防止URL操作攻击
- ASP.NET MVC Tip #20 – 如何对 Data Access 进行单元测试
- 建立一个方法的attribute,可以放在任意方法上,可以自动记录方法出错时的信息,就不用写try 。。cacth. 【注意】 不是在asp.net MVC下,是在普通三层结构下写的的特性。
- 生成URL(而不是链接) Generating URLs (and Not Links) | 在视图中生成输出URL |高级路由特性 | 精通ASP-NET-MVC-5-弗瑞曼
- asp.net mvc 身份验证中返回绝对路径的ReturnUrl