viernes, marzo 28, 2008

Motor de informes dinámicos con iTextSharp (I)

Actualización: he colgado los códigos del proyecto para que podáis jugar con ellos y utilizarlos en vuestros proyectos. Los podéis descargar desde aquí

Buenas de nuevo a todo el mundo que lee este blog (cada día sois más, lo que es de agradecer). He estado muy ocupado últimamente montando mi propio motor de informes dinámicos utilizando la librería iTextSharp para generación de documentos PDF.

La idea inicial era poder disponer (dentro de una aplicación ASP.NET) de un sistema de impresión de informes con tres características principales:
  1. Impresión de informes al vuelo en PDF
  2. Disponer de plantillas en XML para mezclar datos de base de datos y mostrarlos en PDF (también al vuelo).
  3. Poder modificar las plantillas XML desde un editor
Con estos requisitos me puse manos a la obra con iText# (una librería excelente) y os explico cómo lo plantee por si os sirve para vuestros desarrollos o como punto de partida para otras cosas.

Impresión de Informes al vuelo en PDF
Ésta fue la parte más fácil y más complicada a la vez. La más complicada porque me pegué de lleno con la librería y la más fácil cuando aprendí los entresijos de la misma. Para imprimir informes en PDF al vuelo basta con seguir el siguiente ciclo:

















La idea consiste en generar los datos a imprimir en la pagina aspx donde esté el usuario. Esos datos los guardamos en la sesión y llamamos a la pantalla MostrarInforme.aspx. Esta pantalla recupera los datos de la sesión y se los pasa a la clase GeneradorPDF.cs que se encarga de crear el PDF (en memoria) en base a los datos pasados. El método que genera el PDF devuelve un objeto del tipo MemoryStream a la página MostrarInforme.aspx, el cual es renderizado en pantalla mediante un Response.OutputStream.Write().

Dicho así, la verdad es que no aclaro demasiado el tema, así que vamos a ver el código que siempre ayuda más.

El Código
Lo primero fue plantearme el alcance de esta parte del motor de informes. Lo que queríamos era poder imprimir cualquier tabla de cualquier pantalla con cualquier tipo de campos y número de una forma común y fácil. Con este objetivo en mente, lo inicial fue definir las variables de sesión en las que se iban a pasar los datos desde las páginas a la pantalla MostrarInforme.aspx:
  • DataTable con los datos
  • DataTable con los nombres de las columnas de la tabla que se quieren imprimir y el nombre que se le pone a cada una de ellas en el informe.
  • Orientación de la página para poder imprimir en apaisado y en vertical
  • Anchos (en porcentaje) de las columnas que se van a imprimir
  • Nombre del listado que aparecerá en la cabecera
1. Preparar la Impresión
Con estas variables de sesión tenemos información suficiente para poder imprimir el informe. Un ejemplo de cómo se preparan las variables de sesión en una pantalla es el siguiente:

















En este método lo que hago es crear las variables de sesión necesarias (casi todo está con enumerados y struct).
  1. TABLA_DATOS: Para el Datatable con todos los datos.
  2. ORIENTACION_PAGINA: para decirle si queremos imprimir en vertical o en horizontal.
  3. CAMPOS_A_IMPRIMIR: para indicar aquellas columnas del datatable de datos que queremos imprimir y el nombre que queremos que aparezca en la cabecera de dicha columna en el informe. Aquí utilizo un datatable tipado DtCamposListadoPDF.
  4. ANCHOS_DE_COLUMNA: array de enteros con los anchos de cada una de las columnas (en el mismo orden) que he introducido en CAMPOS_A_IMPRIMIR.
  5. TITULO_LISTADO: Título que va a llevar el informe.
2. Llamar a MostrarInforme.aspx
Una vez que tenemos las variables de sesión, llamaremos a la pantalla MostrarInforme.aspx desde nuestra aplicación. Para abrirla hay varias opciones (Javascript, inyectando código). A mi lo que me interesaba era que se mostrará el PDF en una nueva ventana, así que llamo a la página desde Javascript, con un window.open.

También se puede hacer con:



El código de MostrarInforme.aspx es el siguiente:













Todo se realiza en el Page_Load() porque vamos a devolver un Stream al navegador. Lo primero que hacemos es recuperar las variables de sesion que la página ha creado. Obtengo también la ruta de una imagen que va a aparecer en plan logo en los informes para pasarla al método.
Una vez obtenidos los datos llamo al método estático GenerarListado que va a devolver un MemoryStream con el PDF.

Con el MemoryStream lo que hago es renderizarlo en la pantalla mediante el objeto Response. Vamos a ver cómo genero el PDF al vuelo.

3. Generar el PDF
El método GenerarListado de la clase GeneradorPDF es el que realmente se encarga de crear dinámicamente el informe PDF.

Los using que tenemos que utilizar son:







En la primera parte del método definimos el objeto Document que va a contener el PDF, la orientación y enlazamos el objeto MemoryStream con el objeto Document para indicarle que vamos a realizar las operaciones en memoria.




















En segundo lugar llamamos al método IncluirElementosComunes para crear la cabecera del informe, el título y para que incluya el logo:




El código de este método es el siguiente:




















En este método creamos la cabecera del informe, una tabla con el logo en una celda y con el título del listado y la fecha de impresión en la otra. Además, en este método abrimos la escritura en el documento con la instrucción docPdf.Open().

Volviendo al método general, las siguientes instrucciones lo que van a hacer es ir creando una tabla, con tantas columnas como le hemos pasado en sesión y la vamos rellenando con los datos del Datatable de datos.






















Vamos iterando sobre los datatables e incluyendo la información. En esta parte, cada uno dará al informe el aspecto que más le guste. Al final incluimos la tabla en el documento PDF.

Para finalizar lo que tenemos que hacer es capturar los errores, cerrar el PDF y devolver el stream.









Una muestra de un informe en PDF generado por esta función es el siguiente:























Final
En la próxima entrega veremos como realizar las plantillas XML con parámetros y cómo mezclarlas con datos de bases de datos de una forma dinámica. Finalmente, en la tercera entrega veremos como editar las plantillas y cómo crearnos traductores XML y HTML para dichas plantillas.

Espero que os haya interesado este primer artículo y que lo aprovechéis para vuestros desarrollos.

martes, marzo 04, 2008

Firmar una llamada SOAP con C# y ASP.NET

En este artículo voy a intentar explicar cómo firmar una llamada SOAP utilizando la extensión para servicios web WS 3.0 de Microsoft. Como siempre, lo primero es un poco de teoría y luego veremos cómo pasar esta teoría a la práctica.

Es recomendable que os leáis el artículo anterior a este sobre certificados digitales de la FNMT puesto que haremos uso de lo que en él comentaba.

1. Teoría
La idea es poder realizar una llamada a un servicio web externo (una administración, por ejemplo) asegurando que el contenido de la misma no ha sido manipulado en el camino. El ejemplo más claro es el de una aplicación web que se integra con la administración, por lo que tiene que firmar las peticiones con el certificado de la FNMT del cliente que en ese momento este usando la aplicación.








La idea es que nosotros seamos una especie de pasarela de certificados de nuestros clientes a los servicios web de la administración o de otro sitio. Para realizar esto tendremos que firmar las llamadas SOAP con el certificado usando el estándar XMLSignature.

2. ¿Cómo lo hacemos?
Para hacerlo, lo primero que tenemos que hacer es descargarnos las extensiones de Servicios Web de Microsoft (WSE 3.0) que podéis encontrar en la dirección:

http://www.microsoft.com/downloads/details.aspx?FamilyID=018a09fd-3a74-43c5-8ec1-8d789091255d&displaylang=en

Una vez instalado en nuestro equipo, tendremos una nueva opción en Visual Studio para el trabajo con las extensiones de Web Services.

¿Cómo funciona WSE 3.0?
El centro del tema este son las políticas y los filtros. Cuando realizamos una llamada a un servicio web, ésta es interceptada por una clase que define lo que hay que realizar con dicha llamada antes de que salga. Cuando recibimos una respuesta, la respuesta es interceptada igualmente, y lo mismo pasa cuando definimos los servicios web.




Donde nosotros vamos a trabajar es en la clase de políticas de seguridad, que será donde se firme la petición.

Paso 1: Habilitar nuestro web para WSE3.0
Lo primero es habilitar nuestro sitio web para que utilice WSE3.0. Al habilitarlo, las clases proxy que se creen estarán preparadas para las extensiones de WSE. Tendremos que crearnos un sitio web en el Visual Studio y en las propiedades del sitio web veremos cómo aparece una nueva opción "WSE Settings 3.0". Al pinchar se abre la ventana:
























Marcamos la primera opción para habilitar las opciones del cliente. La segunda opción es para habilitar las opciones cuando nosotros somos el servicio web.

Paso 2: Agregar la referencia a WSE
Agregar una referencia a la librería Microsoft.Web.Services3 a nuestro proyecto.

Paso 3: Agregar la referencia web
Agregaremos la referencia web al servicio con el que nos queramos comunicar.

Paso 4: Crear la política de seguridad
Vamos a crear la clase que controla la entrada y salida de mensajes SOAP desde el cliente. Esta clase tiene que heredar de
SecurityPolicyAssertion
















La clase tiene cuatro métodos, uno para cada situación: salida/entrada para el cliente y salida/entrada para el servicio. A nosotros el que nos interesa es el de la salida para el cliente, el resto lo ponemos a null.

La sección de imports es la siguiente:












En el constructor le pasamos el certificado del cliente (luego veremos cómo llamamos a esta clase) para poder utilizarlo en el método de salida del cliente. En este método lo que hacemos es instanciar una nueva clase que hereda de SendSecurityFilter que es la que realmente firma la petición:












En esta clase lo que hacemos es obtener el token del certificado y añadirle la firma al mensaje SOAP, haciendo override del método SecureMessage. Con esto tenemos prácticamente hecho todo.

Paso 5: Realizar la llamada
Nos creamos una página aspx desde la que vamos a realizar la llamada. Lo primero será obtener el certificado del cliente (ver el artículo anterior), una vez obtenido tendremos que asociar la política que hemos definido con la llamada al servicio web:
























En este caso, he creado un servicio web con un método llamado "CalculaSuma" el cual suma dos números que se le pasan. En un botón llamado "btnInvocar" de la página le asocio el código para llamar al servicio web. Lo que hacemos es instanciar la clase proxy que se creó al agregar la referencia web y le asociamos la política que hemos creado.

Al realizar la llamada, la política intercepta el mensaje de salida, ejecuta el método "SecureMessage()" y firma la petición. Podemos activar el modo Trace con el asistente de WSE y ver la salida para comprobar que se ha firmado la petición, incluyéndose un nuevo elemento en la llamada SOAP llamado
.

3. Fin
Espero que os haya gustado y servido este post. No ha sido muy exhaustivo pero como punto de partida para la utilización de XMLSignature en SOAP puede ser bueno.