31 octubre 2008

Crea tu propio servidor HTTP (3)

Si bien hemos aprendido a que el navegador pida nombre de usuario y clave para entrar en nuestras webs privadas, la forma más natural de entrar a una página web suele ser dándose de alta en un formulario HTML para luego entrar con sus datos.

En esta ocasión no vamos a utilizar por ahora las propiedades de autenticación que vimos en artículos anteriores con AResponseInfo.AuthRealm ni vamos controlar la sesión con la clase TIdHTTPSession.

Vamos a comenzar a ver un ejemplo de cómo crear nuestro propio foro. En este artículo vamos a controlar el registro y la entrada de usuarios.

CREANDO LAS PÁGINAS WEB DEL FORO

Para crear un foro necesitamos una página web de entrada que permita hacer login al usuario así como darse de alta en nuestro foro. Esta va a ser nuestra página web de entrada:

El código fuente de la página se puede hacer con el bloc de notas de Windows (clic para ampliar):


La página contiene un formulario que recoge el usuario y su contraseña para luego pulsar el botón Enviar. Esta página hay que guardarla con el nombre index.html.

Cuando el usuario pulse el enlace Registrar entonces saltará a esta otra página:


Cuyo código fuente es el siguiente:


Ahora es cuando tenemos que entrar en faena y programar nuestro servidor HTTP.

CREANDO LA APLICACIÓN SERVIDOR

La ventana del servidor es prácticamente la misma que vimos en los artículos anteriores:


Lo que vamos a cambiar va a ser el evento OnCommandGet del componente TIdHTTPServer:

procedure TFServidorHTTP.ServidorCommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
sDocumento: String;
begin
Log.Lines.Add( ARequestInfo.RemoteIP + ': ' +
ARequestInfo.Command + ARequestInfo.Document );

Log.Lines.Add( 'Parámetros: ' + ARequestInfo.Params.Text );

if ARequestInfo.Document = '/registrar' then
RegistrarUsuario( ARequestInfo, AResponseInfo );

if ARequestInfo.Document = '/entrar' then
Entrar( ARequestInfo, AResponseInfo );

// ¿Va a entrar a la página principal?
if ARequestInfo.Document = '/' then
AResponseInfo.ServeFile( AContext, ExtractFilePath( Application.ExeName ) + 'index.html' )
else
begin
// Cargamos la página web que vamos a enviar
sDocumento := ExtractFilePath( Application.ExeName ) +
Copy( ARequestInfo.Document, 2, Length( ARequestInfo.Document ) );

// ¿Existe la página que ha solicitado?
if FileExists( sDocumento ) then
AResponseInfo.ServeFile( AContext, sDocumento )
else
// No hemos encontrado la página
AResponseInfo.ResponseNo := 404;
end;

AResponseInfo.CloseConnection := True;
end;

Este evento se compone de las siguientes partes:

1º Mostramos en la ventana del servidor que página web nos solicita el usuario y los parámetros de la misma:

Log.Lines.Add( ARequestInfo.RemoteIP + ': ' +
ARequestInfo.Command + ARequestInfo.Document );

Log.Lines.Add( 'Parámetros: ' + ARequestInfo.Params.Text );

Los parámetros serán los datos que el usuario ha rellenado en el formulario web antes de pulsar el botón Enviar.

2º Si el usuario pulsa los botones de registrarse en la página o hacer login entonces envío a cada uno a su procedimiento correspondiente para dar más claridad al código:

if ARequestInfo.Document = '/registrar' then
RegistrarUsuario( ARequestInfo, AResponseInfo );

if ARequestInfo.Document = '/entrar' then
Entrar( ARequestInfo, AResponseInfo );

3º En caso de que el usuario haya solicitado otra página al servidor se la mandamos normalmente:

// ¿Va a entrar a la página principal?
if ARequestInfo.Document = '/' then
AResponseInfo.ServeFile( AContext, ExtractFilePath( Application.ExeName ) + 'index.html' )
else
begin
// Cargamos la página web que vamos a enviar
sDocumento := ExtractFilePath( Application.ExeName ) +
Copy( ARequestInfo.Document, 2, Length( ARequestInfo.Document ) );

// ¿Existe la página que ha solicitado?
if FileExists( sDocumento ) then
AResponseInfo.ServeFile( AContext, sDocumento )
else
// No hemos encontrado la página
AResponseInfo.ResponseNo := 404;
end;

AResponseInfo.CloseConnection := True;
end;

CONTROLANDO EL REGISTRO DE USUARIOS

El procedimiento encargado de dar de alta los usuarios tiene que comprobar si el usuario ya rellenado correctamente su nombre, la contraseña y si las contraseñas coinciden:

procedure TFServidorHTTP.RegistrarUsuario( ARequestInfo: TIdHTTPRequestInfo;
AResponseInfo: TIdHTTPResponseInfo );
var
sNombre, sPassword, sPassword2, sError, sUsuarios: String;
Usuarios: TStringList;
begin
sNombre := ARequestInfo.Params.Values['nombre'];
sPassword := ARequestInfo.Params.Values['password'];
sPassword2 := ARequestInfo.Params.Values['password2'];

if sPassword <> sPassword2 then
sError := '<h3>Las contraseñas no coinciden.</h3>';

if sPassword2 = '' then
sError := '<h3>Debe repetir la contraseña.</h3>';

if sPassword = '' then
sError := '<h3>No ha introducido la contraseña.</h3>';

if sNombre = '' then
sError := '<h3>No ha introducido el nombre del usuario.</h3>';

// Abrimos la lista de usuarios
sUsuarios := ExtractFilePath( Application.ExeName ) + 'usuarios.txt';
Usuarios := TStringList.Create;

if FileExists( sUsuarios ) then
Usuarios.LoadFromFile( sUsuarios );

// Comprobamos si el usuario ya ha sido dado de alta en la lista
if sError = '' then
begin
if Usuarios.Values[sNombre] <> '' then
sError := '<h3>El usuario ya existe. Elija otro nombre.</h3>';
end;

if sError <> '' then
AResponseInfo.ContentText := sError
else
begin
Usuarios.Add( sNombre + '=' + sPassword );

AResponseInfo.ContentText := '<h3>Usuario registrado correctamente:<p>' +
'Nombre: ' + sNombre + '<p>' + 'Contraseña: ' + sPassword + '<p>' +
'<a href="http://www.blogger.com/index.html">Entrar al foro.</a></p></h3>';

Usuarios.SaveToFile( sUsuarios );
end;

AResponseInfo.WriteContent;
Usuarios.Free;
end;

En caso de error mostraría su mensaje correspondiente:


En el caso de que se hayan escrito bien todos los datos lo que hago es guardar la lista de usuarios y sus contraseñas en un archivo de texto que se llamará usuarios.txt. También compruebo si el usuario ya ha sido dado de alta con anterioridad.

Una vez que ha sido dado de alta lo muestro en pantalla:


A su vez vemos como la ventana del servidor va controlando lo que manda el navegador:


Al pulsar el enlace Entrar al foro volverá a la pantalla principal para hacer login.

CONTROLANDO LA ENTRADA DE USUARIOS

El procedimiento encargado de entrar en nuestro foro es parecido y más sencillo que el de registro:

procedure TFServidorHTTP.Entrar( ARequestInfo: TIdHTTPRequestInfo;
AResponseInfo: TIdHTTPResponseInfo );
var
sNombre, sPassword, sError, sUsuarios: String;
Usuarios: TStringList;
begin
sNombre := ARequestInfo.Params.Values['nombre'];
sPassword := ARequestInfo.Params.Values['password'];

// Abrimos la lista de usuarios
sUsuarios := ExtractFilePath( Application.ExeName ) + 'usuarios.txt';
Usuarios := TStringList.Create;

if FileExists( sUsuarios ) then
Usuarios.LoadFromFile( sUsuarios );

// Comprobamos si el usuario ya ha sido dado de alta en la lista
if Usuarios.Values[sNombre] = '' then
sError := '<h3>El usuario no existe.</h3>'
else
if Usuarios.Values[sNombre] <> sPassword then
sError := '<h3>La contraseña es incorrecta.</h3>';

if sError <> '' then
AResponseInfo.ContentText := sError
else
AResponseInfo.ContentText := '<h3>Bienvenido al foro ' + sNombre + '<p>' +
'<a href="http://www.blogger.com/index.html">Salir</a></h3>';

AResponseInfo.WriteContent;
Usuarios.Free;
end;

Sólo hay que asegurarse de que el usuario esté dado de alta en nuestra lista y luego lo dejamos pasar:


Todos los usuarios datos de alta quedan almacenados en el archivo usuarios.txt:


En el próximo artículo vamos a implementar el sistema visualización y envío de mensajes al foro por parte de cada usuario.

No he incluido las diferencias respecto a Delphi 7 porque los cambios son los mismos respecto a los anteriores artículos (ServeFile).

Pruebas realizadas en RAD Studio 2007.

Publicidad