It seems a little odd to go to an .aspx page to download a spreadsheet or document. An alternative approach to solving the problem of customizing documents on the server is to intercept requests for particular file extensions and customize the response before it goes out to the client. This time, instead of creating a new .aspx Web form, create a new .ashx handler (see Figure 18.2). Figure 18.2. Creating a custom handler item. The code in Listing 18.2 is essentially identical; the only difference is that because a handler is not an instance of a Web page, we do not have any of the standard page objects, such as Response, Request, and User. Fortunately, the context of the page request is encapsulated in a special "context" object that is passed to the handler. Listing 18.2. Creating a Custom Handler That Edits the Data Island <%@ WebHandler Language="VB" %> Imports System Imports System.Data Imports System.Data.Common Imports System.Data.OleDb Imports System.IO Imports System.Web Imports Microsoft.VisualStudio.Tools.Applications.Runtime Public Class XLSHandler Implements IHttpHandler Const Forbidden As Integer = 403 Public Sub ProcessRequest(ByVal context As HttpContext) _ Implements System.Web.IHttpHandler.ProcessRequest If Not context.User.Identity.IsAuthenticated Then context.Response.StatusCode = Forbidden context.Response.End() Return End If Dim dataset As DataSet = New DataSet() Dim datatable As DataTable = dataset.Tables.Add("Expenses") Dim adapter As OleDbDataAdapter = New OleDbDataAdapter() adapter.SelectCommand = New OleDbCommand( "SELECT [Date], Description, Cost FROM " & _ "Expenses WHERE EmployeeName = """ & _ "context.User.Identity.Name""") Dim connectionString As String = _ ConfigurationManager.ConnectionStrings( _ "expenses").ConnectionString adapter.SelectCommand.Connection = _ New OleDbConnection(connectionString) adapter.Fill(datatable) Dim file As FileStream = New FileStream( _ "c:\INetPub\WWWRoot\expenses\ExpenseReport.XLS", _ FileMode.Open, FileAccess.Read) Dim template() As Byte Try template = New Byte(file.Length) {} file.Read(template, 0, CType(file.Length, Integer)) Finally file.Close() End Try Dim sd As ServerDocument = New ServerDocument( _ template, ".XLS") Try sd.CachedData.HostItems("ExpenseReport.Sheet1"). _ CachedData("EmpName").SerializeDataInstance( _ context.User.Identity.Name) sd.CachedData.HostItems("ExpenseReport.Sheet1"). _ CachedData( "Expenses").SerializeDataInstance( _ dataset) sd.Save() ' "template" still has the original bytes. ' Get the new bytes. template = sd.Document Finally sd.Close() End Try context.Response.ContentType = "application/vnd.ms-excel" context.Response.OutputStream.Write( _ template, 0, template.Length) End Sub Public ReadOnly Property IsReusable() As Boolean _ Implements System.Web.IHttpHandler.IsReusable Get Return False End Get End Property End Class | Finally, to turn this on, add the information about the class and assembly name for the handler to your Web.config file in the application's virtual root, as shown in Listing 18.3. If you want to debug the server-side code, you can add debugging information in the configuration file, too. Listing 18.3. A Web Configuration File to Turn on the Handler <configuration> <system.web> <httpHandlers> <add verb="GET" path="ExpenseReport.xls" type="XLSHandler, XLSHandler"/> </httpHandlers> <compilation debug="true"/> </system.web> </configuration> | Now when the client hits the server, the handler will intercept the request, load the requested file into memory, contact the database, create the appropriate dataset, and serialize the dataset into the data island in the expense reportall without starting Excel. |