Hoy Quiero
escribir con respecto a como crear un servicio web en C bajo el
estándar SOAP utilizando GSOAP y que este conecte a una base de
datos mysql. Lo anterior, debido a que busque información en la WEB
y no encontré mucha documentación y/o ejemplos prácticos.
Esto puede
servir para crear aplicaciones del tipo bpel y que la inteligencia de
negocio sea por el lado del WS y estos se puedan mezclar.
En este
ejemplo, se muestra el desarrollo y ejecución de dos servicios web
básicos uno que cargue datos y el otro que lea datos desde una una
tabla mysql, ademas de dos script que hacen las veces de cliente para
los WS.
Requerimientos.
1) Linux y gcc instalado
2) Librerias de desarrollo
mysql.
3) mysql
4) gsoap (yo utilizo la
version 2.7)
5) PHP5 instalado para
hacer los clientes.
6) Apache instalado con
soporte cgi activo
Comenzamos.
1) Crear una tabla simple
CREATE
TABLE
`basededatos`.`alumnos`
(
`id`
int(10)
NOT
NULL
AUTO_INCREMENT,
`run`
int(10)
NOT
NULL,
`nombre`
varchar(45)
NOT
NULL,
`apellido1`
varchar(45)
NOT
NULL,
`apellido2`
varchar(45)
NOT
NULL,
`fec_nacim`
date DEFAULT
NULL,
PRIMARY
KEY
(`id`),
UNIQUE
KEY
`run`
(`run`)
)
ENGINE=MyISAM
DEFAULT
CHARSET=utf8
COMMENT='tabla
alumnos'
2) Con la tabla creada,
procedemos a crear dos servicios web una para realizar insert y otro
para select
a) Servicio de insert
Crear un archivo
CreaAlumno.h
//gsoap
ns service name:
CreaAlumno
//gsoap
ns service namespace:
urn:CreaAlumno
//gsoap
ns service location:
http://localhost/cgi-bin/CreaAlumno.cgi
//gsoap
ns schema namespace:
urn:CreaAlumno
struct
Datainput{
char
*run;
char
*nombre;
char
*apellido1;
char
*apellido2;
char
*fec_nacim;
};
struct
ns__getInfoResponse{
char
*recepcion;
};
int
ns__getInfo(struct
Datainput *transaccion,
struct
ns__getInfoResponse *result_soap);
Crear un archivo
CreaAlumno.c
#include
"soapH.h" /* get the gSOAP-generated definitions */
#include
"CreaAlumno.nsmap" /* get the gSOAP-generated namespace
bindings */
#include
<stdlib.h>
#include
<stdio.h>
#include
<string.h>
#include
<time.h>
#include
<mysql/mysql.h>
#define
PATH "/var/log/CreaAlumno/"
#define
server "localhost"
#define
user "root"
#define
password ""
#define
database "basededatos"
#define
tabla "alumnos"
int
main()
{
struct
soap soap;
soap_init(&soap);
time_t tiempo =
time(0);
struct
tm *tlocal
=
localtime(&tiempo);
char
fechaActual[128];
strftime(fechaActual,128,"%Y%m%d",tlocal);
char
fechaHora[128];
strftime(fechaHora,128,"%Y%m%d%H%M%S",tlocal);
char
logf1[1000000];
char
logf2[1000000];
char
logf3[1000000];
sprintf(logf1,"%s%s%s",PATH,fechaActual,"_entrada.log");
sprintf(logf2,"%s%s%s",PATH,fechaActual,"_salida.log");
sprintf(logf3,"%s%s%s",PATH,fechaHora,"_fordebug.log");
soap_set_recv_logfile(&soap,
logf1);
soap_set_sent_logfile(&soap,
logf2);
soap_set_test_logfile(&soap,
logf3);
soap_serve(&soap);
}
int
ns__getInfo(struct
soap *soap,
struct
Datainput *transaccion,
struct
ns__getInfoResponse *result_soap)
{
MYSQL *conn;
MYSQL_ROW row;
MYSQL_RES *retorno;
char
consulta[1000];
char
run[10];
char
nombre[45];
char
apellido1[45];
char
apellido2[45];
char
fec_nacim[10];
sprintf(run,"%.11s",transaccion->run);
sprintf(nombre,"%.46s",transaccion->nombre);
sprintf(apellido1,"%.46s",transaccion->apellido1);
sprintf(apellido2,"%.46s",transaccion->apellido2);
sprintf(fec_nacim,"%.11s",transaccion->fec_nacim);
//conexion
a base
conn =
mysql_init(NULL);
if
(!mysql_real_connect(conn,
server,
user,
password,
database,
0,
NULL,
0))
{
return
soap_sender_fault(soap,
"Error
-1",
"ERROR
Conexion con Base de Datos");
}
//inicia
transaccion
if
(mysql_query(conn,"START
TRANSACTION"))
{
return
soap_sender_fault(soap,
"Error
0",
"Registro
No Ingresado");
mysql_query(conn,"ROLLBACK");
}
sprintf(consulta,"insert
into %s (run,nombre,apellido1,apellido2,fec_nacim) values
('%s','%s','%s','%s','%s')",tabla,run,nombre,apellido1,apellido2,fec_nacim);
if
(mysql_query(conn,consulta))
{
return
soap_sender_fault(soap,
"Error
3",
"Registro
No Ingresado");
mysql_query(conn,"ROLLBACK");
}
int
identificador =
mysql_insert_id(conn);
result_soap->recepcion="OK";
return
SOAP_OK;
//cierra
conexion
mysql_close(conn);
}
Crear un archivo Makefile
CFLAGS=-ggdb
MYSQLFLAGS=`mysql_config
--libs`
SOAPDIR=/home/usuario/Descargas/gsoap-2.7/gsoap
all: CreaAlumno
CreaAlumno:
$(SOAPDIR)/bin/linux386/soapcpp2
-c
CreaAlumno.h
gcc
-D
DEBUG CreaAlumno.c
$(SOAPDIR)/stdsoap2.c
soapC.c
soapServer.c
-o
CreaAlumno.cgi -O3
-I$(SOAPDIR)
-L
$(MYSQLFLAGS)
$(CFLAGS)
Luego desde consola, se
compila utilizando make y si todo sale bien, debiese salir algo
parecido a:
usuario@usuario:/home/usuario/crea_alumnos$
make
** The gSOAP Stub and
Skeleton Compiler for C and C++ 2.7.9l
** Copyright (C)
2000-2007, Robert van Engelen, Genivia Inc.
** All Rights Reserved.
This product is provided "as is", without any warranty.
** The gSOAP compiler is
released under one of the following three licenses:
** GPL, the gSOAP public
license, or the commercial license by Genivia Inc.
Saving soapStub.h
Saving soapH.h
Saving soapC.c
Saving soapClient.c
Saving soapClientLib.c
Saving soapServer.c
Saving soapServerLib.c
Using ns service name:
CreaAlumno
Using ns service style:
document
Using ns service encoding:
literal
Using ns service location:
http://localhost/cgi-bin/CreaAlumno.cgi
Using ns schema namespace:
urn:CreaAlumno
Saving CreaAlumno.wsdl Web
Service description
Saving
CreaAlumno.getInfo.req.xml sample SOAP/XML request
Saving
CreaAlumno.getInfo.res.xml sample SOAP/XML response
Saving CreaAlumno.nsmap
namespace mapping table
Saving ns.xsd XML schema
Compilation successful
Si todo sale OK en el paso
anterior, ya podríamos probar el WS desde consola, pare ello, se
debe modificar el archivo CreaAlumno.getInfo.req.xml agregando data a
los nodos.
<?xml version="1.0"
encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns="urn:CreaAlumno">
<SOAP-ENV:Body>
<ns:getInfo>
<transaccion>
<run>11111111</run>
<nombre>felipe</nombre>
<apellido1>aaaaa</apellido1>
<apellido2>aaaaa</apellido2>
<fec-nacim>20110101</fec-nacim>
</transaccion>
</ns:getInfo>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Posteriormente y desde
consola
usuario@usuario:/home/usuario/crea_alumnos$./CreaAlumno.cgi
< CreaAlumno.getInfo.req.xml
Y revisamos el retorno que
entrega OK.
Status: 200
OK
Server: gSOAP/2.7
Content-Type: text/xml;
charset=utf-8
Content-Length: 418
Connection: close
<?xml version="1.0"
encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns="urn:CreaAlumno"><SOAP-ENV:Body><ns:getInfoResponse><recepcion>OK</recepcion></ns:getInfoResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
b) Servicio de consula
(select)
Crear un archivo
BuscaAlumno.h
//gsoap
ns service name:
BuscaAlumno
//gsoap
ns service namespace:
urn:BuscaAlumno
//gsoap
ns service location:
http://localhost/cgi-bin/BuscaAlumno.cgi
//gsoap
ns schema namespace:
urn:BuscaAlumno
struct
Datainput{
char
*run;
};
struct
ns__getInfoResponse{
char
*nombre;
char
*apellido1;
char
*apellido2;
char
*fec_nacim;
};
int
ns__getInfo(struct
Datainput *transaccion,
struct
ns__getInfoResponse *result_soap);
Crear un archivo
BuscaAlumno.c
#include
"soapH.h" /* get the gSOAP-generated definitions */
#include
"BuscaAlumno.nsmap" /* get the gSOAP-generated namespace
bindings */
#include
<stdlib.h>
#include
<stdio.h>
#include
<string.h>
#include
<time.h>
#include
<mysql/mysql.h>
#define
PATH "/var/log/BuscaAlumno/"
#define
server "localhost"
#define
user "root"
#define
password ""
#define
database "basededatos"
#define
tabla "alumnos"
int
main()
{
struct
soap soap;
soap_init(&soap);
time_t tiempo =
time(0);
struct
tm *tlocal
=
localtime(&tiempo);
char
fechaActual[128];
strftime(fechaActual,128,"%Y%m%d",tlocal);
char
fechaHora[128];
strftime(fechaHora,128,"%Y%m%d%H%M%S",tlocal);
char
logf1[1000000];
char
logf2[1000000];
char
logf3[1000000];
sprintf(logf1,"%s%s%s",PATH,fechaActual,"_entrada.log");
sprintf(logf2,"%s%s%s",PATH,fechaActual,"_salida.log");
sprintf(logf3,"%s%s%s",PATH,fechaHora,"_fordebug.log");
soap_set_recv_logfile(&soap,
logf1);
soap_set_sent_logfile(&soap,
logf2);
soap_set_test_logfile(&soap,
logf3);
soap_serve(&soap);
}
int
ns__getInfo(struct
soap *soap,
struct
Datainput *transaccion,
struct
ns__getInfoResponse *result_soap)
{
MYSQL *conn;
MYSQL_ROW row;
MYSQL_RES *retorno;
char
consulta[1000];
char
run[10];
sprintf(run,"%.11s",transaccion->run);
//conexion
a base
conn =
mysql_init(NULL);
if
(!mysql_real_connect(conn,
server,
user,
password,
database,
0,
NULL,
0))
{
return
soap_sender_fault(soap,
"Error
-1",
"ERROR
Conexion con Base de Datos");
}
sprintf(consulta,"select
nombre,apellido1,apellido2,fec_nacim from %s where run =
%s",tabla,run);
if
(mysql_query(conn,consulta))
{
return
soap_sender_fault(soap,
"Error
3",
"No
pude rescatar dato");
}
retorno =
mysql_store_result(conn);
while((row=mysql_fetch_row(retorno)))
{
result_soap->nombre=row[0];
result_soap->apellido1=row[1];
result_soap->apellido2=row[2];
result_soap->fec_nacim=row[3];
}
return
SOAP_OK;
mysql_close(conn);
}
Crear un archivo Makefile
CFLAGS=-ggdb
MYSQLFLAGS=`mysql_config
--libs`
SOAPDIR=/home/usuario/Descargas/gsoap-2.7/gsoap
all: BuscaAlumno
BuscaAlumno:
$(SOAPDIR)/bin/linux386/soapcpp2
-c
BuscaAlumno.h
gcc
-D
DEBUG BuscaAlumno.c
$(SOAPDIR)/stdsoap2.c
soapC.c
soapServer.c
-o
BuscaAlumno.cgi -O3
-I$(SOAPDIR)
-L
$(MYSQLFLAGS)
$(CFLAGS)
Luego desde consola, se
compila utilizando make y si todo sale bien, debiese salir algo
parecido a:
usuario@usuario:/home/usuario/busca_alumnos$
make
** The gSOAP Stub and
Skeleton Compiler for C and C++ 2.7.9l
** Copyright (C)
2000-2007, Robert van Engelen, Genivia Inc.
** All Rights Reserved.
This product is provided "as is", without any warranty.
** The gSOAP compiler is
released under one of the following three licenses:
** GPL, the gSOAP public
license, or the commercial license by Genivia Inc.
Saving soapStub.h
Saving soapH.h
Saving soapC.c
Saving soapClient.c
Saving soapClientLib.c
Saving soapServer.c
Saving soapServerLib.c
Using ns service name:
BuscaAlumno
Using ns service style:
document
Using ns service encoding:
literal
Using ns service location:
http://localhost/cgi-bin/BuscaAlumno.cgi
Using ns schema namespace:
urn:BuscaAlumno
Saving BuscaAlumno.wsdl
Web Service description
Saving
BuscaAlumno.getInfo.req.xml sample SOAP/XML request
Saving
BuscaAlumno.getInfo.res.xml sample SOAP/XML response
Saving BuscaAlumno.nsmap
namespace mapping table
Saving ns.xsd XML schema
Compilation successful
Si todo sale OK en el paso
anterior, ya podríamos probar el WS desde consola, pare ello, se
debe modificar el archivo BuscaAlumno.getInfo.req.xml
<?xml version="1.0"
encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns="urn:BuscaAlumno">
<SOAP-ENV:Body>
<ns:getInfo>
<transaccion>
<run>11111111</run>
</transaccion>
</ns:getInfo>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Ahora ponemos en consola
usuario@usuario:/home/usuario/busca_alumnos$./BuscaAlumno.cgi
< BuscaAlumno.getInfo.req.xml
Y revisamos el retorno que
entrega OK.
Status: 200
OK
Server: gSOAP/2.7
Content-Type: text/xml;
charset=utf-8
Content-Length: 504
Connection: close
<?xml version="1.0"
encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns="urn:BuscaAlumno"><SOAP-ENV:Body><ns:getInfoResponse><nombre>feli</nombre><apellido1>aaaaa</apellido1><apellido2>aaaaa</apellido2><fec-nacim>2011-01-01</fec-nacim></ns:getInfoResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
c) Con los servicios
creados y probados, ya podemos mover (utilizando cuenta root) los cgi
a su ubicación definitiva que para mi es path /usr/lib/cgi-bin/ (la
ruta se define en el archivo de configuración de apache con soporte
para cgi)
Finalmente, se realizan
los clientes PHP de prueba.
a) script cliente de
carga:
<?php
define("path_wsdl",
"/home/usuario/crea_alumnos/CreaAlumno.wsdl");
define("metodo",
"getInfo");
define("location",
"http://localhost/cgi-bin/CreaAlumno.cgi");
define("trace",1);
function
CreaAlumno($run,$nombre,$apellido1,$apellido2,$fec_nacim)
{
$metodoIdentificacion
=
metodo;
$servicio=path_wsdl;
$parametros=array();
$parametros['location']=location;
$parametros['trace']=trace;
$datos['run']
=
$run;
$datos['nombre']
=
$nombre;
$datos['apellido1']
=
$apellido1;
$datos['apellido2']
=
$apellido2;
$datos['fec-nacim']
=
$fec_nacim;
$parametros['transaccion']=
$datos;
try {
$client
=
new
SoapClient($servicio,
$parametros);
$result
=
$client->$metodoIdentificacion($parametros);
$salida
=
"REQUEST:\n"
.
$client->__getLastRequest()
.
"\n";
$salida
.=
"REQUEST
HEADERS:\n"
.
$client->__getLastRequestHeaders()
.
"\n";
$salida
.=
"Response:\n"
.
$client->__getLastResponse()
.
"\n";
$salida
.=
"Response:\n"
.
$client->__getLastResponseHeaders()
.
"\n";
return
$salida;
}
catch (SoapFault
$fault)
{
$error
=
"REQUEST:\n"
.
$client->__getLastRequest()
.
"\n";
$error
.=
"REQUEST
HEADERS:\n"
.
$client->__getLastRequestHeaders()
.
"\n";
$error
.=
"Response:\n"
.
$client->__getLastResponse()
.
"\n";
$error
.=
"Response:\n"
.
$client->__getLastResponseHeaders()
.
"\n";
return
$error;
}
}
print_r(CreaAlumno('3333333','felipe','aaaaa','bbbbb','20110101'));
?>
Al ejecutar el script
debiese retornar algo parecido a:
REQUEST:
<?xml version="1.0"
encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:CreaAlumno"><SOAP-ENV:Body><ns1:getInfo><transaccion><run>3333333</run><nombre>felipe</nombre><apellido1>aaaaa</apellido1><apellido2>bbbbb</apellido2><fec-nacim>20110101</fec-nacim></transaccion></ns1:getInfo></SOAP-ENV:Body></SOAP-ENV:Envelope>
REQUEST HEADERS:
POST
/cgi-bin/CreaAlumno.cgi HTTP/1.1
Host: localhost
Connection: Keep-Alive
User-Agent:
PHP-SOAP/5.3.2-1ubuntu4.9
Content-Type: text/xml;
charset=utf-8
SOAPAction: ""
Content-Length: 378
Response:
<?xml version="1.0"
encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns="urn:CreaAlumno"><SOAP-ENV:Body><ns:getInfoResponse><recepcion>OK</recepcion></ns:getInfoResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
Response:
HTTP/1.1
200
OK
Date: Fri, 21
Sep 2012
19:07:43
GMT
Server: Apache/2.2.14
(Ubuntu)
Connection: close
Content-Length: 418
Vary: Accept-Encoding
Content-Type: text/xml;
charset=utf-8
b) script cliente de
consulta
<?php
define("path_wsdl",
"/home/usuario/busca_alumnos/BuscaAlumno.wsdl");
define("metodo",
"getInfo");
define("location",
"http://localhost/cgi-bin/BuscaAlumno.cgi");
define("trace",1);
function
BuscaAlumno($run)
{
$metodoIdentificacion
=
metodo;
$servicio=path_wsdl;
$parametros=array();
$parametros['location']=location;
$parametros['trace']=trace;
$datos['run']
=
$run;
$parametros['transaccion']=
$datos;
try {
$client
=
new
SoapClient($servicio,
$parametros);
$result
=
$client->$metodoIdentificacion($parametros);
$salida
=
"REQUEST:\n"
.
$client->__getLastRequest()
.
"\n";
$salida
.=
"REQUEST
HEADERS:\n"
.
$client->__getLastRequestHeaders()
.
"\n";
$salida
.=
"Response:\n"
.
$client->__getLastResponse()
.
"\n";
$salida
.=
"Response:\n"
.
$client->__getLastResponseHeaders()
.
"\n";
return
$salida;
}
catch (SoapFault
$fault)
{
$error
=
"REQUEST:\n"
.
$client->__getLastRequest()
.
"\n";
$error
.=
"REQUEST
HEADERS:\n"
.
$client->__getLastRequestHeaders()
.
"\n";
$error
.=
"Response:\n"
.
$client->__getLastResponse()
.
"\n";
$error
.=
"Response:\n"
.
$client->__getLastResponseHeaders()
.
"\n";
return
$error;
}
}
print_r(BuscaAlumno('3333333'));
?>
Al ejecutar el script
debiese retornar algo parecido a:
REQUEST:
<?xml version="1.0"
encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:BuscaAlumno"><SOAP-ENV:Body><ns1:getInfo><transaccion><run>3333333</run></transaccion></ns1:getInfo></SOAP-ENV:Body></SOAP-ENV:Envelope>
REQUEST HEADERS:
POST
/cgi-bin/BuscaAlumno.cgi HTTP/1.1
Host: localhost
Connection: Keep-Alive
User-Agent:
PHP-SOAP/5.3.2-1ubuntu4.9
Content-Type: text/xml;
charset=utf-8
SOAPAction: ""
Content-Length: 269
Response:
<?xml version="1.0"
encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns="urn:BuscaAlumno"><SOAP-ENV:Body><ns:getInfoResponse><nombre>felipe</nombre><apellido1>aaaaa</apellido1><apellido2>bbbbb</apellido2><fec-nacim>2011-01-01</fec-nacim></ns:getInfoResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
Response:
HTTP/1.1
200
OK
Date: Fri, 21
Sep 2012
19:36:04
GMT
Server: Apache/2.2.14
(Ubuntu)
Connection: close
Content-Length: 506
Vary: Accept-Encoding
Content-Type: text/xml;
charset=utf-8
Nota:
Los PATH de logs definidos
en los archivos C, se no son creados de forma automatica, deben ser
creados de forma manual y dar los respectivos permisos.