Sunday, October 04, 2009

Crear Reportes PDF con ASP.NET MVC


Estos días estaba leyendo el libro Professional ASP.NET MVC 1.0 de wrox y creía que este artículo posiblemente será muy útil para aquellos que comienzan con este alucinante framework implementando el patrón Model-View-Controller y no tienen la ayuda de third parties que les generen reportes (Yo nunca fui muy amigo de Crystal Reports, así que preferí otras opciones) o que simplemente quieran usar un proyecto open source. Usualmente, necesitaremos mostrar nuestra información en un formato manejable, elegante y liviano, por lo que utilizaremos la librería Report.net que nos genera reportes PDF mediante puro código. Para aquellos que no conocen su uso, pueden ver mis posts previos.
Esencialmente, la API de la librería arma la estructura de un documento PDF y renderiza el resultado en la página web con un content-Type de tipo application/pdf.


Este artículo se divide en dos:


  • La modificación del proyecto Report que viene con el download de la librería para permitir la generación del reporte en MVC

  • La creación de un proyecto ASP.NET MVC con un reporte de ejemplo

Comencemos. Al momento de escribir este artículo, la librería no soportaba la generación del reporte en MVC, por lo que tuve que hacer una pequeña modificación al mismo. En el transcurso de los días trataré de incluir esto a la librería. Primeramente debemos descargar el proyecto de Report.net de sourceforge. Abrimos el mismo en VS.

Veremos la estructura de toda la librería. Aquí el archivo que nos interesa es el archivo Report.cs que se encuentra en la superficie del proyecto. Lo abrimos y buscamos el método:


public static void ResponsePDF(Report report, System.Web.UI.Page page)


Aquí explico un poco. Este método es utilizado para poder generar el reporte en pantalla. Para ello, tiene como parámetros de entrada un objeto Root.Reports.Report y un System.Web.UI.Page.



La entidad Report será utilizada para llamar al método Create que implementa cualquier reporte que creemos heredando de la misma Root.Reports.Report.


La página es solamente utilizada para acceder al objeto Response, que es el que modela la respuesta del servidor a la página. En este tenemos los elementos necesarios para mostrar un output en la página resultante. Aquí podemos ver que se limpia el contenido anterior, se hace el set del contentType a application/pdf, se crea el reporte cargando el OutputStream de la página y llamándose a End del objeto response.


Mi aporte consiste en adicionar un nuevo método que llamé:

public static void ResponsePDF4MVC(Report report, System.Web.HttpContext currentContext)



public static void ResponsePDF4MVC(Report report, System.Web.HttpContext currentContext)
{
currentContext.Response.Clear();
currentContext.Response.ContentType = "application/pdf";

if (!(report.formatter is PdfFormatter))
{
throw new ReportException("PDF formatter required");
}
if (report.page_Cur == null)
{
report.Create();
}

report.formatter.Create(report, currentContext.Response.OutputStream);
currentContext.Response.End();
}

Ya desde aquí, vemos que la firma cambió. En vez de utilizar un objeto System.Web.UI.Page, usaremos un HttpContext. Debido a que ASP.NET MVC no maneja ninguna Page e interactúa directamente con los HttpHandlers (MvcHandler) . Esto nos da la posibilidad de hacer lo que deseemos con el output resultante. Tal como en el bloque anterior, este método utiliza el parámetro tipo HttpContext para manejar su objeto Response. Por lo demás, el manejo del método es igual a su padre :D.


Compilamos el proyecto y tendremos nuestro assembly modificado.


Bien, ya tenemos lo necesario para crear nuestro primer reporte ASP.NET MVC. Antes de continuar, será necesario que revisemos si tenemos ASP.NET MVC instalado para continuar. Si no lo tenemos, podremos descargarlo desde el sitio de asp.net.

Una vez sepamos que podemos continuar, abriremos Visual Studio y creamos un proyecto ASP.NET MVC (File -> New -> Project)


Nombramos a nuestro proyecto como PDFGenerator y definimos la ruta a grabar. Lo siguiente que veremos será la pantalla preguntándonos si queremos crear pruebas unitarias. Para efectos de este ejemplo, seleccionaremos la opción No, do not create a unit test Project.



A continuación, tendremos un proyecto ASP.NET MVC inicial generado para nosotros.



Haremos referencia a nuestro assembly Reports.dll

Ahora, iremos por partes. Primero el Modelo, luego el Controller y finalmente la vista.


Model

Conceptualmente, el modelo define la estructura de nuestros datos, las reglas y la lógica de negocio. Es aquí donde crearemos nuestro objeto reporte, el mismo tendrá toda la estructura a ser mostrada en pantalla posteriormente.

Sobre la carpeta Models creamos una nueva clase a la que llamaremos SampleReport. Éste debe heredar de Root.Reports.Report.



public class SampleReport : Report

No olvidemos añadir la referencia a
using Root.Reports;

Bueno, ahora voy a armar un reporte simple, por lo que mi código terminaría así:

public class SampleReport : Report
{
FontDef _fontDefinition;
FontProp _fontProperty;

protected override void Create()
{
// Este llamado es muy importante para generar la página
NewPage();
// Inicialia las propiedades del reporte
InitializeReportProperties();
// Imprime mensaje
PrintHelloWorldMessage();
}

private void InitializeReportProperties()
{
// Este estilo de fuente debe ser definido sólo una vez
_fontDefinition = new FontDef(this, FontDef.StandardFont.TimesRoman);
_fontProperty = new FontProp(_fontDefinition, 10, Color.Navy);
}

private void PrintHelloWorldMessage()
{
page_Cur.AddCT_MM(100, 50, new RepString(_fontProperty, "Good morning joy!"));

page_Cur.AddCT_MM(100, 70, new RepString(_fontProperty, "Implementing ASP.NET MVC"));
}
internal void NewPage()
{
new Root.Reports.Page(this);
}

}

Explicación:

Hacemos un Override del método Create, esto nos permitirá tener el punto de partida para crear el reporte. El método NewPage nos permite crear una nueva página. Sí o sí tendremos que hacer esta llamada. Inicializamos las propiedades del reporte con InitializeReportProperties y finalmente imprimimos nuestro mensaje: “Buen día alegría!” :D.
Una vez que hemos definido el reporte eso es todo con relación a la vista.


Controller

La parte de controller es mucho más sencilla, ya que casi todo el trabajo ha sido hecho. Lo único que tendremos que hacer será añadir un método público denominado HelloWorld en nuestro HomeController. Ésta será tomada como acción y accedida desde la URL.
Dentro de la acción, instanciaremos nuestro reporte y haremos la llamada al método ResponsePDF4MVC (el que previamente creamos en el proyecto Report). Quedaría así:


public ActionResult HelloWorld()
{
SampleReport report = new SampleReport();
RT.ResponsePDF4MVC(report, this.HttpContext.ApplicationInstance.Context);

return View();
}

Notemos que la llamada a ResponsePDF4MVC tiene como parámetro el contexto de ApplicationInstance, ya que this.HttpContext es una implementación especial de tipo HttpContextBase. Hacemos la llamada a la vista y eso es todo en nuestro controller.

View

En esta parte, añadiremos una vista llamada HelloWorld.aspx bajo la carpeta Views/Home:


Esta página me sirve para hacer el llamado a la acción, el sistema de ruteo haga la llamada al controller identificando la acción HelloWorld y éste a su vez devuelva el resultado a una vista con el mismo nombre. Es importante el nombrado de views y acciones, esto por convención, nos permitirá hacer la comunicación entre los elementos del patrón.

Ya casi acabando, modificaremos la página Index.aspx, eliminamos el contenido de MainContent por con un simple link que apunte a nuestro HelloWorld.aspx. Entonces nuestra página index.aspx quedaría así:



<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>


<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Home Page
</asp:Content>

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>My first ASP.NET MVC PDF</h2>
<p>
Watch our <%= Html.ActionLink("Hello world", "HelloWorld") %> example
</p>

</asp:Content>


Si nos fijamos en el código que tiene el llamado a ActionLink, veremos que vamos a mostrar solamente el texto Hello world acompañado de la acción a ser invocada. Por dentro, el sistema de ruteo se encargará de llamar al método HelloWorld y nuestro controller sabrá que tiene que existir una página también llamada HelloWorld.aspx.

Finalmente, compilamos nuestro código le damos debug (dejamos que se pueda modificar el archivo web.config para hacer debugging) para ver nuestra página de la siguiente manera:


Presionando sobre el link Hello world tendremos finalmente nuestro tan ansiado reporte PDF en ASP.NET MVC



Bueno, eso es todo amigos. Mi primer reporte PDF en ASP.NET MVC implicó que conozca la API de reporte Report.net, también implicó el conocimiento de conceptos del patrón MVC y la práctica con ASP.NET MVC para juntar todo. En sí no es complicado. Como lo dije antes, el construir el reporte “a mano” puede ser un poco largo para quienes están acostumbrados a usar herramientas tipo Crystal, pero si lo vemos del otro lado tendremos una libertad completa para generar contenido en PDF mediante código, sin mencionar que en vez de usar DataTables, podremos actuar directamente con entidades de negocio.

ASP.NET MVC puede ser asimilado rápidamente, la página del framework tiene excelentes artículos para comenzar a aprender y también está la opción de adquirir un libro que hable del tema. Yo recomendaría el libro Professional ASP.NET MVC 1.0, escrito por la gente que diseñó el framework. También está ASP.NET MVC In Action que basa la explicación del modelo en base a Domain Driven Development.

Este ejemplo lo realicé porque se me vino una corazonada una de esas noches de inspiración. Si es que existen algunas inconsistencias conceptuales, les agradezco si pueden comentar al respecto.

Bueno, eso es todo. Espero que esto sirva para complementar a este maravilloso framework MVC y ayudar a quienes lo necesitan.

2 comments:

Carlos said...

Excelente y muy útil.

Anonymous said...

Bonjorno, rodriogri.blogspot.com!
[url=http://viagraenat.pun.pl/ ]Compra viagra generico[/url] [url=http://cialisdkee.pun.pl/ ]Acquistare cialis in Italia[/url] [url=http://viagraycla.pun.pl/ ]Acquisto viagra generico[/url] [url=http://cialisonya.pun.pl/ ]Comprare cialis generico[/url] [url=http://viagrareta.pun.pl/ ]Vendita viagra in Italia[/url] [url=http://cialisybea.pun.pl/ ]Compra cialis online[/url]