Torre de Babel

Cómo crear componentes ActiveX para ASP con Delphi

by Francisco Charte.

Internet Information Server es capaz de generar documentos dinámicos a partir de páginas ASP. Con Delphi 5 podemos crear componentes ActiveX para usar desde estas páginas. Te demostramos cómo hacerlo.

Hubo un tiempo, no hace muchos años, en que las páginas web no eran mas que documentos HTML estáticos, composiciones de texto con algo de color, alguna imagen y unos útiles enlaces. Actualmente, a pesar del poco tiempo transcurrido, la mayoría de esas páginas se han convertido en complejas composiciones, con animaciones, anuncios que cambian, contenido que se actualiza dinámicamente según las preferencias del usuario, etc. Para que se produzca este gran cambio ha sido necesario aportar nuevas soluciones en las dos partes: el cliente y el servidor.

Los documentos que procesa el cliente web no están ya sólo compuestos de HTML, además existen hojas de estilo, guiones en lenguajes de script, HTML dinámico, datos en XML, etc. Todo esto, no obstante, no es suficiente para crear un contenido realmente dinámico, por ejemplo a partir de las preferencias del usuario introducidas en un formulario. El servidor web, que en aquellos tiempos citados se limitaba poco más que a servir los documentos que se le pedían, ha pasado a convertirse en una especie de servidor de aplicaciones. A petición de los clientes, el servidor web puede ejecutar CGIs, funciones almacenadas en librerías de enlace dinámico y código de script en páginas procesadas en la parte servidor. El resultado de esa ejecución, es el nuevo contenido que se envía a los clientes.

Una de las novedades que, en su día, aportó IIS (Internet Information Server), fue la de procesar páginas en el lado servidor antes de enviarlas al cliente. En dichas páginas, conocidas como ASP (ActiveX Server Pages), se incluyen scripts y contenidos reemplazables. El servidor web procesa la página generando un nuevo documento, enviándolo al cliente como código HTML corriente. El contenido dinámico es, por tanto, independiente del cliente que utilice el usuario final.

Aunque en el interior de una página ASP es posible introducir guiones en distintos lenguajes, como VBScript o JavaScript, existen limitaciones de funcionalidad y rendimiento. Por eso, en ocasiones, lo que se hace es crear componentes ActiveX que se encargan de efectuar la mayor parte del trabajo, componentes que son creados y utilizados desde la página ASP. Delphi 5 incorpora las definiciones y asistentes necesarios para crear este tipo de componentes, que aparecen como una alternativa a las clásicas aplicaciones ISAPI y NSAPI que existían desde la versión 3.

Objetos ofrecidos por el servidor

El servidor web, en este caso IIS, recibe una solicitud de un cliente a la que debe dar respuesta. Para permitir que un elemento externo, en este caso un componente ActiveX, tenga acceso a esa información: todos los datos de la solicitud y los de respuesta, IIS ofrece una serie de objetos. Como siempre en Windows, estamos hablando de objetos COM que, en nuestro caso, podemos usar prácticamente como si de cualquier otro objeto Delphi se tratase.

La Figura 1 representa gráficamente y de manera simplificada el proceso en que interviene un componente ActiveX de tipo ASP. El cliente envía una petición al servidor, solicitándole un determinado documento ASP. IIS procesa dicho documento, ejecutando el código que hay en su interior para obtener como resultado un documento HTML. Durante la ejecución, el motor ASP usa un control ActiveX para generar todo o parte de ese contenido dinámico. Sirviéndose de los objetos integrados en el motor ASP, el control puede obtener datos sobre la solicitud, la sesión de trabajo y otros elementos, generando finalmente la respuesta.

Los dos objetos usados más habitualmente son Request y Response, cada uno de los cuales cuenta con varias colecciones y propiedades. Mediante la colección Form del objeto Request, por ejemplo, es posible acceder a los valores introducidos por el usuario en un formulario HTML. La colección Cookies, con la que cuentan ambos objetos, puede utilizarse tanto para leer como para escribir cookies en el sistema del usuario.

Otros objetos interesantes son Application, compartido por todas las sesiones y componentes utilizados por IIS, y Session, útil para gestionar información relativa a la sesión de un solo cliente. La versión 5.0 de IIS, integrada en el próximo Windows 2000, dispone también de un objeto, llamado ObjectContext, que puede utilizarse para controlar transacciones en el ámbito ASP.

Todos estos objetos estarán, en el caso concreto de Delphi, accesibles mediante las propiedades de la clase TASPObject, que servirá como base para crear nuestros propios componentes ActiveX para ASP. En realidad, las propiedades Application, Session, Response y Request siempre devolverán un puntero a una interfaz, como ISessionObject o IResponse. Estas interfaces, codificadas en el módulo Asptlb.pas, cuentan con las propiedades y métodos precisos para añadir cabeceras HTML a la respuesta para el cliente, modificar una cookie o conocer el tipo de cliente.

El nuevo asistente para objetos ASP

En la página ActiveX del Depósito de objetos de Delphi 5 hay disponible un nuevo asistente, identificado por la etiqueta Active Server Object y con el icono que puede verse en la Figura 2. Como todos los asistentes que generan componentes COM, éste debe ser usado previa creación de una librería o ejecutable de tipo ActiveX.

El asistente consta de una simple ventana, la mostrada en la Figura 3, en la que se dan varias opciones para personalizar el objeto ASP a crear. Tras introducir el nombre, podremos elegir el modelo de gestión de hilos y el modo de creación de objetos. En el apartado Active Server Type existen dos opciones, cuya finalidad es indicar el tipo de servidor con el que funcionará el componente. Por defecto está elegida la primera de ellas, que es la apropiada para IIS 3 y 4. En caso de que estemos usando IIS 5, por ejemplo en Windows 2000, habrá que seleccionar la segunda opción.

Las versiones 3 y 4 de IIS usan dos eventos, llamados OnStartPage y OnEndPage, para comunicar a los objetos ASP el contexto de ejecución. IIS 5, por el contrario, utiliza un contexto de objeto basado en MTS (Microsoft Transaction Server), cuyos servicios se han integrado en COM+ en el caso de Windows 2000. Dependiendo del tipo de servidor, nuestro nuevo componente derivará de TASPObject, para las versiones 3 y 4, o TASPMTSObject, para IIS 5.

Crear un componente ASP en Delphi, a pesar de la existencia de este asistente, no es una tarea realmente sencilla debido a la falta de documentación sobre las clases citadas y sus propiedades. Si recurre a la ayuda de Delphi o la documentación escrita, verá que no es posible encontrar nada más que un mínimo tutorial que enumera los pasos para crear una página ASP simple para crear un objeto ASP. No se describe, por el contrario, cómo hay que usar propiedades como Form o Cookies, ni cómo puede enviarse contenido desde el control ASP al cliente.

En realidad, lo que ha hecho Borland es simplemente traducir el archivo de cabecera Asptlb.h generando un módulo Object Pascal con varias interfaces y clases. El asistente, por su parte, se limita a definir una clase simple derivada de otra y que implementa una determinada interfaz, pero sin ninguna funcionalidad adicional. Cualquier ayuda, por tanto, habrá que buscarla en la documentación propia de IIS, un producto que pertenece a Microsoft.

Manos a la obra

La mejor manera de entender cómo se utiliza un componente ASP es, sin duda, siguiendo un caso práctico. Por eso, en lugar de continuar con la teoría sobre las interfaces, sus propiedades, colecciones, etc., vamos a ponernos manos a la obra creando un ejemplo muy sencillo de partida, que podremos ir completando después.

Los componentes ASP que vamos a crear a continuación estarán alojados en una librería ActiveX, por lo que tendrás que comenzar seleccionando la opción File|New ... para abrir el Depósito de objetos, haciendo doble clic sobre el elemento ActiveX Library de la página ActiveX. Este paso lo daremos una sola vez, de tal forma que una sola DLL almacenará los diversos componentes.

Acto seguido habrá que añadir el primer componente ASP, usando para ello el asistente anteriormente citado. Tan sólo modificaremos el nombre, que será HolaMundoASP. Al cerrar el asistente se abrirá el editor de librerías de tipos ActiveX, que usaremos, como se aprecia en la Figura 4, para añadir un nuevo método a la interfaz IHolaMundoASP. Este método, llamado HolaMundo, se limitará a emitir el clásico saludo como respuesta a la solicitud de los clientes.

El módulo de código generado por el asistente será similar al del Listado 1. Lo único que haremos será completar la implementación del método HolaMundo. Observa cómo se usa la propiedad Response, concretamente su método Write, para enviar la cadena al cliente. Finalizada la implementación, habrá que compilar el proyecto para generar la correspondiente DLL, que copiaremos en la carpeta scripts o equivalente de nuestra configuración de servidor IIS. Como todo componente COM, éste deberá ser apropiadamente registrado para poder utilizarse. Con este fin puedes usar la utilidad regsvr32.

unit HolaMundoASPUnit;

interface

uses
  ComObj, ActiveX, AspTlb, HolaMundo_TLB, StdVcl;

type
  THolaMundoASP = class(TASPObject, IHolaMundoASP)
  protected
    procedure OnEndPage; safecall;
    procedure OnStartPage(const AScriptingContext: IUnknown); safecall;
    procedure HolaMundo; safecall;
  end;

implementation

uses ComServ;

procedure THolaMundoASP.OnEndPage;
begin
  inherited OnEndPage;
end;

procedure THolaMundoASP.OnStartPage(const AScriptingContext: IUnknown);
begin
  inherited OnStartPage(AScriptingContext);
end;

procedure THolaMundoASP.HolaMundo; // Al invocar a este método
begin
    Response.Write('¡Hola mundo!'); // emitimos el saludo
end;

initialization
  TAutoObjectFactory.Create(ComServer, THolaMundoASP, Class_HolaMundoASP,
    ciMultiInstance, tmApartment);
end.

Aparte del módulo de código, el asistente ASP también ha creado una página ASP que nos servirá como plantilla para poder probar el funcionamiento del componente. Observa que la mayor parte de esa página está compuesta de código HTML, a excepción del script delimitado por las parejas de caracteres . Estos delimitadores permiten al servidor IIS identificar las partes que tiene que procesar, sustituyendo su contenido por el resultado que genere dicho proceso.

En nuestro caso particular, como puedes ver en el Listado 2, el código se limita a crear una copia del componente HolaMundoASP que se encuentra en la DLL HolaMundo, llamando a continuación a uno de sus métodos. La página ASP habrá que copiarla a la carpeta raíz de IIS, habitualmente wwwroot, u otra que hayas establecido para realizar estas pruebas. Hecho esto, no hay mas que abrir nuestro cliente web habitual y solicitar el documento ASP al servidor, como puedes ver en la Figura 5. Ten en cuenta que no puedes abrir directamente el documento ASP con el cliente web, sino que ha de ser el servidor el que procese la petición para poder generar el resultado. Es necesario, por tanto, tener instalado y activo el servidor IIS.

<HTML>
<BODY>
<TITLE> Testing Delphi ASP </TITLE>
<CENTER>
<H3> You should see the results of your Delphi Active Server method below </H3>
</CENTER>
<HR>
<% Set DelphiASPObj = Server.CreateObject("HolaMundo.HolaMundoASP") 
   DelphiASPObj.HolaMundo
%>
<HR>
</BODY>
</HTML>

La interfaz IResponse

Cuando usamos la propiedad Response del objeto ASP, en el ejemplo anterior para enviar una cadena simple al cliente, lo que estamos usando es una interfaz COM, concretamente la interfaz IResponse. Ésta cuenta con un importante número de métodos que, en su mayor parte, tienen por finalidad manipular la respuesta que se enviará al cliente. El método Write, por ejemplo, añade una cadena al final de ese contenido.

En el ejemplo anterior, el resultado que obtiene el cliente web es una combinación del código HTML existente en el documento ASP más la cadena insertada por el componente ASP. Éste, no obstante, podría previamente haber eliminado todo el contenido mediante el método Clear. El envío del contenido se efectúa cuando se ha terminado de procesar el documento ASP. Una llamada al método End, sin embargo, forzaría el envío inmediato de la respuesta, finalizando dicho proceso.

Otros métodos, accesibles como propiedades en el caso de Delphi, permiten conocer el estado actual, saber si el cliente está o no conectado, establecer el tipo de contenido a enviar y acceder a las cookies, entre otras operaciones.

Aunque en el ejemplo anterior se ha usado el método Write para enviar una cadena simple, realmente podemos usarlo para enviar cualquier código. Podríamos, por ejemplo, generar una tabla HTML a partir de una base de datos, escribiéndola en el objeto Response para enviarla al cliente. La tabla podría generarse manualmente, como se hace en el componente ListaTablaASP que hemos añadido al proyecto anterior.

No hay un trabajo de diseño, puesto que el componente TTable se crea dinámicamente como puede verse en el Listado 3, asignando las propiedades necesarias para acceder a una de las tablas de ejemplo que acompañan a Delphi. La Figura 6 es un ejemplo del resultado obtenido.

procedure TListaTablaASP.ListaTabla;
var
    Table1: TTable;
begin
    try
      Table1 := TTable.Create(Nil); // Creamos el componente
      // y accedemos a la base de datos de ejemplo
      Table1.DatabaseName := 'DBDEMOS';
      Table1.TableName := 'country.db';
      Table1.Open; // abrimos la tabla

      Response.Write('<TABLE>'); // iniciamos la tabla HTML
      while not Table1.EOF do begin // hasta llegar al final
          Response.Write('<TR>' + // iniciamos una fila
          // con tres columnas
          '<TD>' + Table1.FieldByName('Name').AsString + '</TD>' +
          '<TD>' + Table1.FieldByName('Continent').AsString + '</TD>' +
          '<TD>' + Table1.FieldByName('Capital').AsString + '</TD>' +
          '</TR>'); // fin de la fila

          Table1.Next; // pasamos a la siguiente
      end;
      Response.Write('</TABLE>'); // fin de la tabla
      Table1.Close; // cerramos la tabla
      Table1.Free; // y la destruimos
    except // si se produce una excepción lo más posible es que no se
        Response.Write('Fallo al abrir la tabla'); // pueda abrir
    end;
end;

La interfaz IRequest

Mediante la propiedad Request del objeto ASP es posible acceder a la información de solicitud efectuada por el cliente, leyendo campos de formularios HTML, cookies, variables y otros valores interesantes, como la dirección remota del cliente, el método de petición, etc.

La mayor parte de las propiedades accesibles mediante Request, como Form, Cookies, ServerVairables, QueryString o ClientCertificate, devuelven siempre como referencia un puntero a una interfaz IRequestDictionary. Esto significa que todas esas propiedades permiten acceder a una colección de datos, usando el típico operador [ ] de Object Pascal y como índice una cadena con el nombre de la cookie, campo o elemento que corresponda. Puesto que la propiedad Item de la interfaz IRequestDictionary es la propiedad por defecto, es posible escribir directamente expresiones como la siguiente:

Request.Form[Nombre]

En este caso está accediéndose al campo Nombre del formulario HTML desde el que se supone el cliente ha enviado la solicitud. De forma análoga, podríamos recuperar una cookie o identificar al cliente.

Podemos ver esto en la práctica con un sencillo ejemplo. Partimos de un documento HTML como el del Listado 4, con una formulario HTML en el que se supone el usuario debe introducir un identificador y una contraseña. Fíjate en la acción que ejecutará el formulario, que será Registro.asp, y en el nombre de los dos campos del formulario, ya que serán esos nombres los que debamos usar posteriormente para acceder a los datos.

<HTML>
<BODY>
<FORM NAME="Registro" METHOD=POST ACTION="Registro.asp">
<CENTER>
<TABLE>
  <TR>
    <TD><B>Identificador de usuario</B></TD>
    <TD><INPUT TYPE=TEXT NAME="Identificador"></TD>
  </TR>
  <TR>
    <TD><B>Contraseña</B></TD>
    <TD><INPUT TYPE=PASSWORD NAME="Contrasena"</TD>
   </TR>
   <TR>
    <TD align=middle colSpan=2>
<INPUT TYPE=SUBMIT NAME="Entrar" VALUE="Entrar"></TD>
   </TR>
</TABLE>
</CENTER>
</FORM>
</BODY>
</HTML>

Lo siguiente será añadir un nuevo componente ASP al proyecto, componente que irá acompañado del documento Registro.asp, desde el que se llamará al método Registro de este nuevo componente. En dicho método, como se observa en el Listado 5, utilizamos la colección Form de la propiedad Request para acceder a los datos introducidos por el usuario en el formulario. Esta información la usamos para componer una respuesta, aunque en la práctica la usaríamos para validar la entrada contra una base de datos de usuarios, permitiendo o denegando el acceso.

procedure TRegistro.Registro;
begin // Recuperamos los valores del formulario y los devolvemos al cliente
    Response.Write('Dices ser <B>' + String(Request.Form['Identificador']) +
        '</B> e intentas entrar con la clave <B>' +
        String(Request.Form['Contrasena']) + '</B>');
end;

Observa cómo se realiza una conversión de tipo para poder mostrar los datos recuperados del formulario. Hay que tener en cuenta que estamos trabajando con objetos COM automatizables, con colecciones en las que los elementos siempre son considerados OleVariant, de ahí que sea preciso convertir al tipo que corresponda en cada paso. En las Figuras 7 y 8 puedes ver el formulario y la respuesta obtenida.

Un punto de partida

La información facilitada en este tutorial, junto con los ejemplos que le acompañan, son tan sólo un punto de partida en el desarrollo de componentes para páginas ASP. Ya sabes, sin embargo, cómo crear un componente de este tipo, cómo enviar respuestas al cliente y cómo recuperar datos de un formulario. De forma análoga, podrías leer valores almacenados en el sistema del cliente (las famosas cookies) y realizar cualquier otro proceso. Dada la escasa información facilitada por Borland, si deseas profundizar en el tema de ASP y el uso de componentes ActiveX ASP, tendrás que recurrir a la documentación de Microsoft. En la sede MSDN, http://msdn.microsoft.com, podrás encontrar mucha información.

Algunas de las operaciones efectuadas en los componentes de ejemplo, como la creación de una respuesta a partir de los datos del formulario, pueden realizarse de forma más sencilla directamente en el script ASP. Lo que se persigue con los ejemplos, no obstante, es mostrarte cómo realizar esas mismas operaciones desde Delphi, aunque los componentes ASP tienen sentido cuando el trabajo a realizar es más complejo o necesita ser más eficaz. Las distintas acciones, además, pueden codificarse como métodos de un mismo componente, en lugar de crear varios componentes como hemos hecho nosotros.