Inicio > Aplicación, Delphi, WebService > (3/5) Generación de un cliente (WebService) en Delphi.

(3/5) Generación de un cliente (WebService) en Delphi.

Share Button

En las dos entradas anteriores de esta serie, hemos planteado el problema y hemos visto cómo generar un WebService en PHP que nos permita acceder a nuestra Base de Datos. Una vez completada la parte Servidor, vamos a comenzar a desarrollar las aplicaciones cliente que van a consumir los datos.

“ROADMAP” DE LAS ENTRADAS

Siguiendo con nuestra “hoja de ruta”, vamos a desarrollar varios clientes de escritorio en Windows, utilizando diferentes alternativas y posibilidades de las que disponemos.

web-service-overview-1

CLIENTE 1: Delphi6 + Indy + LkJSON

Nuestra primera opción va a ser una aplicación (creada en Delphi 6) que acceda al primer webservice que hemos creado aquí y para ello utilizaremos las librerías Indy.

La dirección base es la siguiente, http://neftali.clubdelphi.com/agenda/listado.php; A esa dirección le añadiremos los parámetros necesarios para obtener los datos que necesitemos.

JSONEn este caso la solución es muy simple y basta con utilizar el componente  TIdHTTP para acceder a la dirección deseada, incluyendo parámetros, según la respuesta que queramos obtener.
Para tratar la información que nos devuelve el servidor  (en JSON) utilizaremos la librería LkJSON de Leonid Koninin, que podéis encontrar en SourceForge, para extraer los datos y recorrerlos de una forma más sencilla. Aprovecharemos para llenar un TClientDataset  que nos permita mostrarlos en pantalla utilizando componentes de Base de Datos.

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
var
  TS:TStrings;
  FResponse: string;
  obj:TlkJSONbase;
  vLevel, i:Integer;
  Str, sURL:String;
  k:Word;
begin
  // Crear el componente para los envíos
  Self.Fhttp := TIDHttp.Create(nil);
  // ini
  Self.Fhttp.HandleRedirects := True;
  Self.Fhttp.ReadTimeout := 5000;
 
  // Crear la estructura de respuesta
  Self.FResp := TStringStream.Create('');
  TS := TStringList.Create;
  // Tratar los parámetros
  if (edtIDUsuario.Text <> '') then begin
    i := StrToIntDef(edtIDUsuario.Text, -5);
    if (i = -1) then begin  // Todos los usuarios
      sURL := DIR_BASE;
    end
    else begin  // Buscamos por ID
      sURL := DIR_BASE + '?user=' + IntToStr(i);
      cbAllUsers.Checked := False;
    end;
  end
  else begin
    if (edtNumRegistros.Value = -1) or (edtNumRegistros.Value > 0)then begin
      sURL := DIR_BASE + '?num=' + IntToStr(edtNumRegistros.Value);
    end
    else begin
      sURL := DIR_BASE;
    end;
  end;
 
  // realizar la peticion
  try
    Self.Fhttp.Post(sURL, TS, Self.FResp);

 

Como veis el código es bastante simple y fácil de entender. Creamos el componente TidHTTP, añadimos a la dirección base del WebService los parámetros, según los datos que ha seleccionado el usuario y lanzamos la petición.

Habría que capturar los posibles errores en la llamada, pero en este caso nos vamos a centrar en la respuesta y en los casos en que es correcta.

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  // realizar la peticion
  try
    Self.Fhttp.Post(sURL, TS, Self.FResp);
    // recoger la respuesta (string)
    FResponse := Self.FResp.DataString;
    // La hacemos visible
    mmResponse.Lines.Text := FResponse;
 
    // Conseguir la cadena formateada (componente LkJSON).
    obj := TlkJSONbase.Create();
    // proteccion
    try
      vLevel := 0;
      obj := TlkJSON.ParseText(FResponse);
      // obtener respuesta formateada
      mmResponseOK.Lines.Text := GenerateReadableText(obj, vLevel);
      // Parsear la respuesta para almacenar los datos en un TClientDataset
      ParseResponse(TlkJSONobject(obj));
    finally
      FreeAndNil(obj);
    end;

Imagen9

Lo que hacemos en este caso es recuperar las respuesta en formato texto (pues es JSON) y después de formatearla (no es necesario, simplemente es para poder ver el resultado de forma más legible), llamamos al procedimiento ParseResponse, que lo que hace es recorrer los registros devueltos y almacenarlos en un TClientDataset (para poder mostrarlos en un DBGrid estandard).

Imagen8

El procedimiento ParseResponse  no tienen ningún secreto, y es el que tenéis a continuación.

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Extraer usuarios de la respuesta
procedure TForm1.ParseResponse(AObjResp: TlkJSONobject);
var
  objUserList:TlkJSONlist;
  objUser, objtmp:TlkJSONobject;
  i:integer;
  Str:String;
begin
  // Limpiar el contenido
  cds.EmptyDataSet;
  // Objeto usuarios?
  if (AObjResp.IndexOfName('Usuarios') <> -1) then begin
    // Lo almaceamos en un tipo "lista"
    TlkJSONBase(objUserList) := AObjResp.Field['Usuarios'];
    // Recorrer la lista de usuarios.
    for i := 0 to (objUserList.Count - 1) do begin
      // Objeto usuario
      TlkJSONBase(objUser) := objUserList.Child[i];
      TlkJSONBase(objtmp) := objUser.FieldByIndex[0];
      // Añadimos un registro al CDS
      cds.Append;
      try
        // Recoger los campos del objeto
        cds.FieldByName('id').AsString := VarToStr(objtmp.Field['id'].Value);
        cds.FieldByName('nombre').AsString := VarToStr(objtmp.Field['nombre'].Value);
        cds.FieldByName('apellidos').AsString := VarToStr(objtmp.Field['apellidos'].Value);
        cds.FieldByName('telefono').AsString := VarToStr(objtmp.Field['telefono'].Value);
        cds.FieldByName('extension').AsString := VarToStr(objtmp.Field['extension'].Value);
        cds.FieldByName('departamento').AsString := VarToStr(objtmp.Field['departamento'].Value);
        cds.FieldByName('interno').AsString := VarToStr(objtmp.Field['interno'].Value);
        cds.FieldByName('email').AsString := VarToStr(objtmp.Field['email'].Value);
        // Grabar
        cds.Post;
      finally
        cds.Cancel;
      end;
    end;
  end;
end;

Aquí tenéis un pequeño vídeo del programa de ejemplo funcionando.

Ejemplo1_funcionando

Al final os dejo los links para descargar los fuentes y el ejecutable del proyecto.

 

CLIENTE 2 (Delphi XE5 + Componentes REST):

Para probar este primer Webservice (http://neftali.clubdelphi.com/agenda/listadoREST2.php) he creado otro cliente; En este caso utilizando Delphi XE5.

Las últimas versiones de Delphi (creo recordar que desde la XE2 en adelante) ya poseen integrada una librería para tratar JSON y también componentes específicos para conectarse con servidores REST. En concreto, para este ejemplo vamos a ver que, con pocas líneas de código (casi ninguna) podemos no  sólo tratar la respuesta del servidor, sino al igual que hemos hecho antes, rellenar un TClientDataset con los datos recibidos.

Para la petición y la respuesta utilizaremos los componentes REST que he comentado:

  •     RESTRequest1: TRESTRequest;
  •     RESTClient1: TRESTClient;
  •     RESTResponse1: TRESTResponse;

Y para convertir los datos, tenemos otro componente llamado “TRESTResponseDataSetAdapter”, que como bien dice su nombre nos hace de adaptador entre la respuesta (en este caso en formato JSON) y un Dataset (TClientDataset).

La forma de utilizar los tres primeros componentes es sencilla.

Imagen10

El componente TRestRequest posee la propiedad Client y la propiedad Response, que se utilizan para asignar los otros 2 componentes.
Si vemos en contenido del DFM (para estos tres componentes) veremos que no tienen nada más que configurar:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  object RESTRequest1: TRESTRequest
    Client = RESTClient1
    Params = <>
    Response = RESTResponse1
    OnAfterExecute = RESTRequest1AfterExecute
    Left = 24
    Top = 440
  end
  object RESTClient1: TRESTClient
    Accept = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
    Params = <>
    HandleRedirects = True
    Left = 80
    Top = 440
  end
  object RESTResponse1: TRESTResponse
    Left = 136
    Top = 440
  end

Sólo nos queda realizar la búsqueda utilizando TRestClient según los parámetros. Para ello yo he creado 2 procedimientos simples:

0
1
    procedure SearchUsuarios(AId:integer); overload;
    procedure SearchUsuarios(ANombre:string); overload;

Cuya implementación es muy básica:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
procedure TFMain.SearchUsuarios(AId: integer);
var
  sURL:string;
begin
  sURL := URL_BASE + '?user=' + IntToStr(AId);
  RESTClient1.BaseURL := sURL;
  // Busqueda
  RESTRequest1.Execute;
end;
 
procedure TFMain.SearchUsuarios(ANombre: string);
var
  sURL:string;
begin
  sURL := URL_BASE + '?nom=' + ANombre;
  RESTClient1.BaseURL := sURL;
  // Busqueda
  RESTRequest1.Execute;
end;

Como veis, lo único que hacemos es añadir el parámetros necesario a la URL_BASE (dirección de nuestro WebService) y lanzar la petición.
Una vez que la petición se completa, la respuesta llega a nuestro componente TRestResponseAdapter, que “une” nuestro componente de respuesta (TRestResponse) con el componente Dataset (TClientDataSet). Si Hemos realizado la definición de campos de forma correcta, tendremos automáticamente los registros que devuelve la petición en nuestro TClientDataset (facil y límpio).

Imagen11

Para este ejemplo, he modificado el fichero del Webservice (el nuevo está en listadoREST.php) para que los parámetros sean más fáciles de recoger con el componentes TRESTResponseDatasetAdapter, y además lo he ampliado (en el fichero listadoREST2.php) con una búsqueda (un nuevo parámetro) por el campo nombre. Para ello se tiene en cuenta un nuevo parámetro nom, y se realiza un LIKE sobre la tabla de usuarios, utilizando caracteres comodín.

Aquí podéis ver un pequeño vídeo del proyecto en el IDE y del mismo funcionando.

 

CLIENTE 3 (Delphi XE5 + Componentes SOAP)

 

wsdl3Como tercer ejemplo voy a realizar una aplicación, utilizando los componentes SOAP. En este caso necesito importar el fichero WSDL y generar la unit que me permitirá acceder a los métodos creados en el Servidor de forma sencilla. En la entrada en que generamos nuestro servidor, vimos al final una variante utilizando la librería nuSOAP, para poder generar de forma automática este fichero. Este fichero será el que utilizaremos para importarlo desde Delphi.

 

Para importar el fichero WSDL lo podemos realizar desde Delphi utilizando el asistente “Import WSDL”; Se encuentra en el menú File/New/Other en la pestaña de WebServices.

Aquí podéis ver dos imágenes del proceso de Importación. Imagen12 Una vez que tenemos la unit generada queda añadida a nuestro proyecto. Podemos ver, ya en el momento de la generación las estructuras complejas definidas para los datos y los métodos definidos en nuestro WebService.

05t5

 

Para realizar la comunicación vamos a utilizar el componente THTTPRIO. Configuramos la dirección del fichero WSDL y las propiedades Port y Service que nos ofrece el componente.

 

Imagen13

 

Definimos un array  de objetos (utilizando las clases definidas en el WebService) para recibir la lista de usuarios y realizamos la llamada utilizando algunos de los métodos definidos: ConsultaUsuario o ConsultaUsuarios.

 

0
1
2
3
4
5
6
7
8
9
10
11
12
13
var
  arr:ArregloDeEstructuras;
  info:Estructura;
  l, i:Integer;
begin
  // Según el rellenado llamamos a uno u otro
  if (edtId.Text <> '') then begin
    arr := (HTTPRIO1 as Consulta_de_usuariosPortType).ConsultaUsuario(
      StrToInt(edtId.text));
  end
  else begin
    arr := (HTTPRIO1 as Consulta_de_usuariosPortType).ConsultaUsuarios(
      edtNombre.text);
  end;

Una vez obtenemos la respuesta, lo único que tenemos que hacer es recorrer el array, para tener todos los usuarios que nos ha devuelto la consulta.

ejemplo

Espero que os haya sido útil e interesante. Como los comentarios, sugerencias, críticas  y demás, serán bienvenidas.
Os adjunto los links para descargar los tres proyectos (código fuente y ejecutables):

Pues hasta aquí hemos llegado hoy.

Hasta la próxima.

Share Button
  1. Domingo
    jueves, 6 de febrero de 2014 a las 16:30 | #1

    Muy interesante. No conocía LkJSON, tendre que echarle un vistazo

  2. Jhonny
    jueves, 6 de febrero de 2014 a las 17:03 | #2

    Un articulo fenomenal, muchas gracias por compartir tu conocimiento con todos nosotros :)

  3. Neftalí
    jueves, 6 de febrero de 2014 a las 18:22 | #3

    @Domingo
    Está muy bien “parida” y funciona muy bien. Recomendable 100%.

  4. Neftalí
    jueves, 6 de febrero de 2014 a las 18:22 | #4

    @Jhonny
    Gracias.

  5. viernes, 7 de febrero de 2014 a las 08:31 | #5

    Excelente série de artículos, muy bien explicados y que hacen que hasta parezca facil. Un excelente trabajo.

  6. lunes, 10 de febrero de 2014 a las 09:25 | #6

    ¿ Consideras la opción de explicar la inserción de datos desde los clientes ? y quizá… ¿ algo de gestión de seguridad para el acceso ?

  7. Neftalí
    lunes, 10 de febrero de 2014 a las 10:00 | #7

    @Jose A.
    Hola Jose A.
    Ninguna de las dos cosas estaba prevista, de todas formas, ya estoy montando un “Apéndice Extra” para el final de las entradas, con cosas que me he ido dejando y que me perecen interesates, así que tal vez se podrían incluir ahí.

    En cuanto a la inserción de datos, ya te digo que no tienen ninguna complicación extra respecto a lo que hemos visto. Hay métodos de consulta (a base de SQL) en los clientes que hemos creado, de la misma forma se puede realizar una operación de inserción en la Base de datos utilizando SQL.

    Por ejemplo, para otro proyecto que tengo entre manos, utilizo métodos similares a este. No están en la aplicación “cliente” sino en otra que tiene métodos de “Administración” (por decirlo así).

    //——————————————————————————
    // Insertar un libro en la Base de Datos
    function InsertarLibro($apikey, $idlibro, $ISBD, $autor, $titulo, $genero, $coment) {

    // comprobar la APIkey
    if (! testAPIKey($apikey) == TRUE) {
    return ‘APIKEY vacía o inválida’;
    }

    // otras comprobaciones de seguridad
    //…

    $sql = “INSERT INTO `tf_libros` (`idlibro`, `ISBN`, `autor`, `titulo`, `genero` , `coment`)
    values ($idlibro, ‘$ISBN’, ‘$autor’, ‘$titulo’, ‘$genero’, ‘$coment’)”;

    $conn = dbConnect();
    $result = mysql_query($sql) or die(mysql_error());

    return $sql;
    }
    //——————————————————————————

    En cuanto a la seguridad, utilizo usuario y contraseña en los métodos de administración (además de la APIKEY que ves en este). Más allá de eso no llego (por ahora).

    Un saludo.

  8. socger
    jueves, 27 de marzo de 2014 a las 20:29 | #8

    Perdona, pero en el ejemplo … Cliente Delphi XE5 + componentes REST … en el Edit1.Text donde le guardo la URL para crear la conexión… la he cambiado por un localhost de mi servidor wamp y no alcanzo a comprender porqué no me aparece en el dbGrid los registros que si que me aparecen en el Memo1. Sin embargo cuando vuelvo a poner la dirección correcta a tu url en neftali.clubdelphi.com si que me registra el dbgrid los registros que devuelves.

    No entiendo porque no funciona por que la misma dirección en mi navegador funciona. Devolviendome los resgistros que le he pedido.

    Soy nuevo en XE5, así que tiene que haber algo obvio que no he cambiado en alguno del resto de componentes RESPONSE. ¿Serías tan amable de darme una idea?

    Gracias de antemano y un saludo

  9. socger
    jueves, 27 de marzo de 2014 a las 20:59 | #9

    socger :
    Perdona, pero en el ejemplo … Cliente Delphi XE5 + componentes REST … en el Edit1.Text donde le guardo la URL para crear la conexión… la he cambiado por un localhost de mi servidor wamp y no alcanzo a comprender porqué no me aparece en el dbGrid los registros que si que me aparecen en el Memo1. Sin embargo cuando vuelvo a poner la dirección correcta a tu url en neftali.clubdelphi.com si que me registra el dbgrid los registros que devuelves.
    No entiendo porque no funciona por que la misma dirección en mi navegador funciona. Devolviendome los resgistros que le he pedido.
    Soy nuevo en XE5, así que tiene que haber algo obvio que no he cambiado en alguno del resto de componentes RESPONSE. ¿Serías tan amable de darme una idea?
    Gracias de antemano y un saludo

    A este comentario anterior creo que le pasa que no tenemos el código ni de listadoREST2.php ni de listadoREST.php …. ¿Podría ser esto? … Si es así ¿puedes facilitarmelo?

    Gracias de antemano

  10. Neftalí
    viernes, 28 de marzo de 2014 a las 09:51 | #10

    @socger
    Hola socger.
    Al final de la entrada, donde están el resto de links, he añadido uno nuevo donde están los dos ficheros que me solicitas. A ver si con eso consigues solventar el problema.

    Un saludo.

  11. socger
    viernes, 28 de marzo de 2014 a las 13:40 | #11

    Gracias.

    @Neftalí

  12. Giuliano
    domingo, 6 de abril de 2014 a las 11:13 | #12

    Bueno obtengo el siguiente problema, me puedes ayudar?

    http://i1187.photobucket.com/albums/z387/giulichajari/Dibujo_zps3a8452c3.jpg

    Solo se admite un elemento de nivel superior en el xml

  13. Neftalí
    lunes, 7 de abril de 2014 a las 07:47 | #13

    @Giuliano
    Revisa los parámetros de conexión a la Base de Datos.
    Te está fallando el connect (parece).

    Un saludo.

  14. Giuliano
    martes, 8 de abril de 2014 a las 17:36 | #14

    Lo que sucede es que ahora cambio el mensaje, lo que creo es que aparece como si fuera un erro de php pero en el cuadro de dialogo de delphi?

  15. Neruda
    domingo, 20 de julio de 2014 a las 03:49 | #15

    Neftalí,
    Muy buena la explicación. Al parecer no pude replicar correctamente tu ejemplo en XE5. Descargué la utilería LkJSON, pero no encuentro donde se encuentra definido TJson (TJson. Format)

  16. Neftalí
    lunes, 21 de julio de 2014 a las 09:32 | #16

    @Neruda
    Piensa que las versiones nuevas ya traen disponible librería para JSON.

  17. giuli
    domingo, 24 de agosto de 2014 a las 16:55 | #17

    No has probado utilizar un archivo.ini para tomar los parametros de la conexion? como ip y demas..

  18. Neftalí
    lunes, 8 de septiembre de 2014 a las 08:49 | #18

    @giuli
    Hola Giuli.
    El artículos muestra lo básico para desarrollar el tema. Hay muchas alternativas y mejoras, entre ellas la que comentas tú (y muchas otras), pero no era el momento de desarrollarlas en el artículo, pues la idea era centrarme en el tema que se trata.

    Un saludo.

  19. Eduardo
    lunes, 3 de noviembre de 2014 a las 19:52 | #19

    hola neftali es muy bueno tu aporte ya lo probe con tu servidor y funciona de maravilla, estoy usando delphi 2007, mi duda comienza cuando agarro el servidor para ponerle una base de datos que tengo en localhost gracias al wampp y es cuando no me jala me bota un error que dice:

    “solo se admite un elemento de nivel superior en un documento XML.

    line 2:
    Notice array to string conversi. ”

    que deberia hacer, de antemano gracias por el aporte

  20. Neftalí
    jueves, 6 de noviembre de 2014 a las 11:01 | #20

    @Eduardo
    Hola Eduardo.
    ¿Exactamente cuando es cuando te devuelve este error?
    ¿Al realizar alguna consulta al webservice?
    ¿Qué datos está devolviendo?

  21. Leonardo
    martes, 16 de diciembre de 2014 a las 18:11 | #21

    Hola Neftali, estupendo ejemplo , gracias por compartir.
    Mi consulta es la siguiente, tengo que conectarme con un rest ws de un tercero en el cual
    ha creado un login el cual retorna un token para asi luego consultar otro ws en el cual le tengo que pasar este toker para obtener los resultados.

    Tienes alguna idea como hacerlo o donde puede obtener informacion ?

    Mil gracias

  22. Margarite
    viernes, 19 de diciembre de 2014 a las 01:07 | #22

    Hola, excelente aporte!
    En mi caso yo tengo el fichero WSDL, pero me veo forzada a usar delphi 5 por lo que no existe el “import WSDL”… Ya intenté crear una dll desde el WSDL mediante C# e importarla a delphi, sin embargo, no me reconoce los WS correctamente, ¿tienes alguna sugerencia que me pueda ayudar a implementar la llamada a los servicios teniendo delphi5 y el archivo WSDL?

    De antemano muchas gracias :)

  23. Neftalí
    viernes, 19 de diciembre de 2014 a las 08:26 | #23

    @Margarite
    Hola Margarite.
    Creo recordar haber leído sobre alguna otra herramienta similar; Tal vez deberías revisar los foros y/o stackoverflow.
    Busca sobre “Web Service Toolkit”; Es un paquete que se puede utilizar con Lazarus y creo que también posee la característica de Importar WSDL.
    Otra opción es solicitar ayuda para que alguien te haga la importación con alguna versión superior de Delphi y te de el fichero importado. Es un proceso que apenas lleva unos segundos.

    LINK: http://wiki.freepascal.org/Web_Service_Toolkit

    Un saludo.

  24. Juan ma
    martes, 17 de febrero de 2015 a las 16:22 | #24

    Lo primero gracias. Estoy conectando mediante un cliente mediante Componentes REST, el problema es que el servidor devuelve un fichero pdf y no se como hacer para poder guardarlo en la carpeta que yo quiera. Solo se acceder a la propiedad content del objeto TRESTRESPONSE.

    Me puedes ayudar? Gracias.

  25. martes, 10 de noviembre de 2015 a las 16:11 | #25

    Neftali, abri los ojos con tu ejemplo, garcias es simple pero efectivo.
    No sabia como conectar mis datos en la web, ahora veo con claridad lo que debo hacer. Gracias.

  26. Jeremiselxi
    domingo, 10 de abril de 2016 a las 19:05 | #26

    Buenas tardes Neftali.

    Te felicito por este blog y está serie tan excelente.

    ¿Sabes? Me ha interesado mucho la opción del
    CLIENTE 2 (Delphi XE5 + Componentes REST). Me gustaria que ampliaras mas acerca de estos componentes en un ejemplo como este desde 0 con json pero que los resultado se obtengan en un dbgrid o en cualquier componente. Te comento tambien que aunque la veo más sencilla esta opcion me pasó que descargue este cliente y cambie únicamente la dirección del listado en mi servidor y sólo me trae la información en el memo1 tal como te aparece a ti pero no puedo visualizar la en el dbgrid.

    ¿Podrias decirme que puede hacer estar pasando?

    Gracias de antemano:)
    .

  27. Neftalí
    lunes, 11 de abril de 2016 a las 11:23 | #27

    @Jeremiselxi
    Hola Jeremi, gracias por el comentario.

    En cuanto a la información que recuperas en el memo1, verás que para convertirla o mostrarla en el Grid, lo que hago yo es utilizar un procedimiento que “parsea” la información recuperada y rellena un ClientDataset, con los registros encontrados.

    Si has conseguido que la información se muestre en el memo (es decir que la has recuperado del servidor correctamente), lo más fácil debería ser esa conversión. Para ello revisa el procedimiento “ParseResponse” y modifícalo para que se adapte a la respuesta que tú obtienes.

    Un saludo.

  28. viernes, 4 de noviembre de 2016 a las 16:20 | #28

    Buenos días

    Estoy intentando consumir un webservice soap pero al ejecutar el proceso me sale el siguiente mensaje en el RAISE:
    ERemotableException – No se pued eencontrar un método de envío para {}xml

    el código es el siguiente:
    try
    Port := conexionWS as ServiciosWS;
    respuestaWS:=Port.procesarFormula(xml);
    except
    on E: Exception do
    ShowMessage(E.ClassName + ‘ ‘ + E.Message);
    end;

    Agradezco me puedan ayudar con alguna información al respecto.

  29. Octavio Pimentel
    domingo, 4 de diciembre de 2016 a las 07:20 | #29

    Excelentes. articulos son de mucha utilidad, tengo un sistema C/S echo en Delphi que se conecta directamente a una bd mysql y quisiera saber si el uso de estas tecnologías (json, rest) ayudan a mejorar la velocidad de transferencia de datos o si existe alguna ventaja significativa para hacer el cambio del sistema para reemplazar la conexion directa por rest o json, tambien tengo dudas en como se ejecutarian los stored procedures, inserts y updates utilizando el. webservice.
    muchas gracias

  30. lunes, 5 de diciembre de 2016 a las 09:05 | #30

    @Octavio Pimentel
    Hola Octavio.
    No tengo las medidas de tu sistema para poder contestarte temas de velocidad. De todas formas si tu sistema se conecta directamente à la Base de Datos es probable que sea más rápido que esta tecnología. Este sistema se suele utilizar cuando no se tiene acceso directo a la Base de Datos (como suele ser en los servidores públicos).

    En cuanto a los SP’s, inserts o updates la forma sería la misma que el resto de consultas (además en uno de los comentarios superiores, tienes un ejemplo de INSERT). Eso sí, debería implementar algún tipo de seguridad si vas a ejecutar esas sentencias.

    Un saludo.

  31. Tatiana Fuentes
    jueves, 15 de diciembre de 2016 a las 18:08 | #31

    Hola,
    para el caso de “Cliente Delphi6/Delphi XE5 + componentes SOAP”, resulta que ese seguimiento se parece mucho a la version de Delphi 7, y por tanto ya hice lo indicado pero resulta que me sale error con el .dcu y el .pas para ‘soap.invokeRegistry’, agrego el .dcu, y luego el .pas a las librerias internas de delphi, y compilo con f7 y todo parece funcionar… apenas lo corro y hago la consulta me sale un error de tipo ‘C0000005 with message “acces violation at 0x0050c6d2: read of address 0x000000c” ‘. process stoped….
    NO SE QUE HACER YA… Y pues soy nueva en el asunto y necesito aprender ya que necesito hacer un cliente que consuma un web service y consulte constantemente datos
    AYUDA!!
    AYUDA!!
    AYUDA!!

  32. viernes, 16 de diciembre de 2016 a las 08:13 | #32

    @Tatiana Fuentes
    Hola Tatiana.
    Sin ver código es difícil saber qué puede estar pasando. Normalmente los “Access Violations” son accesos no permitidos a memoria. A intentar usar un objeto no inicializado o intentar acceder a uno que ya se ha liberado.
    ¿Has ejecutado paso a paso para saber dónde se produce el error?
    Activa en las opciones la visualización de excepciones en diseño “Notify on Language exceptions”, a ver si eso te da una pista de dónde se produce el fallo.

    Un saludo.

  33. Tatty Fuentes
    jueves, 12 de enero de 2017 a las 21:49 | #33

    HOLA, Germán muchas gracias!.

    mira tengo una duda con el codigo usado para delphi 6 con componentes indy. especificamente con este procedimiento (procedure TForm1.ParseResponse(AObjResp: TlkJSONobject);), pues cuando corro el codigo me dice q hay un error en esta linea (if (AObjResp.IndexOfName(‘Usuarios’) -1) then begin)… te explico: yo tengo el codigo hecho en delphi7 (no creo q eso efecte), la cosa es que creo que tiene que ver con el alias de la trama que me devuelve el webService, pues veo que en (http://neftali.clubdelphi.com/agenda/listado.php) la trama empieza con un alias llamado “Usuarios” y con {} — y ese es el nombre que le asignas a las estas lineas de codigo:
    if (AObjResp.IndexOfName(‘Usuarios’) -1) then begin
    y
    TlkJSONBase(objUserList) := AObjResp.Field[‘Usuarios’];

    por tanto deseo saber si para las tramas que empiezan asi:

    [{“id”:288,”documento”:”11223344″,”nombre”:null,”fecha”:”2000-05-15″,”muestra”:”m11223344″,”sexo”:”M”,”tipoDoc”:”CC”,”nit”:”99999999″,”procedimientos”:[{“sub_id”:769,”codigo”:”A98546″,”cups”:”B98546″,”procedimiento_nombre”:”Colesterol”}, {“sub_id”:689,”codigo”:”A4678″,”cups”:”B4678″,”procedimiento_nombre”:”Triglic\u00e9ridos”}]},
    existe un tratamiento diferente, por lo que creo estariamos tratando con un array… y no sabria que funciones usar de la unidad uLkJson… agradezco tu ayuda.

    los datos efectivamente deseo pasarlos a un dbGrid

  34. viernes, 13 de enero de 2017 a las 08:56 | #34

    @Tatty Fuentes
    Hola Tatiana.
    Tal como comentas, si tu respuesta es diferente a la del ejemplo, tendrás que adaptar el procedimiento ParseResponse a tus datos. Básicamente ese procedimiento es el que convierte la respuesta en formato JSON a un TDataSet (TClientDataset).
    Por lo tanto como tu trama es diferente debes adaptarlo pra que lea correctamente tus datos.

    Un saludo.

  35. Tatty Fuentes
    viernes, 13 de enero de 2017 a las 14:24 | #35

    @Germán Estévez
    German, eso lo entiendo y no hay lio, pero quisiera saber como es el tratamiento para los arrays, ya que tengo muy poca información y los ejemplos que encuentro aplican para las ultimas versiones de delphi y la unidad Json cuenta con otras funcuiones que no cuenta la unidad uLkJson… por ello recurro a vos. para ver si puedes darme una orientación respecto a eso…

  36. viernes, 13 de enero de 2017 a las 14:41 | #36

    @Tatty Fuentes
    Hola.
    En ese mismo ejemplo tienes un ejemplo de arrays. Se hare utilizando los objetos TlkJSONlist.
    De todas formas si no te aclaras, envíame una muestra correcta del JSON que tienes que parsear e intento mirármelo.
    Si el JSON es muy complejo, puedes revisar un proyecto OpenSource que hay llamado JSonToDelphiClass, que parsea un fichero JSON y te genera las clases de Delphi para acceder a él. Pero he de decir que personalmente no lo he usado.

    Un saludo.

  37. viernes, 13 de enero de 2017 a las 14:43 | #37

    @Tatty Fuentes
    También puedes buscar más ejemplos de utilización aquí en la web, porque he usado esa biblioteca en más proyectos, así que puedes ver implementaciones diferentes que la usan.

    http://neftali.clubdelphi.com/?s=lkjSON

  38. Tatty Fuentes
    viernes, 13 de enero de 2017 a las 16:06 | #38

    @Germán Estévez
    German muchas gracias… mira ya he buscado bastante y voy a echar un vistaso a lo que me dices y aconsejas pero te dejo aqui… el formato json que necesito parsear, y esto de verdad me esta estresando jeje y repito lo estoy haciendo en delphi 7…

    este es el formato json en cuestion:

    [{“id_muestra”:297,”paciente_documento”:”11223344″,”paciente_nombre”:null,”fecha_nacimiento”:”2000-05-15″,”numero_muestra”:”m11223344″,”sexo”:”M”,”tipo_id”:”CC”,”nit_empresa”:”99999999″,”procedimientos”:[{“id”:257,”procedimiento_codigo”:”A98546″,”codigo_cups”:”B98546″,”procedimiento_nombre”:”Colesterol”},{“id”:258,”procedimiento_codigo”:”A4678″,”codigo_cups”:”B4678″,”procedimiento_nombre”:”Triglic\u00e9ridos”}]},{“id_muestra”:298,”paciente_documento”:”11335566″,”paciente_nombre”:null,”fecha_nacimiento”:”2000-05-15″,”numero_muestra”:”m11335566″,”sexo”:”M”,”tipo_id”:”CC”,”nit_empresa”:”99999999″,”procedimientos”:[{“id”:259,”procedimiento_codigo”:”A4678″,”codigo_cups”:”B4678″,”procedimiento_nombre”:”Glucosa Sangu\u00ednea “}]}]

  39. lunes, 16 de enero de 2017 a las 10:02 | #39

    @Tatty Fuentes
    Se trata de tomar el primer elemento como un array (que lo es) y a partir de ahí crear elementos de Tipo básico (TlkJSONobject) o de tipo Lista (TlkJSONlist) según sea el elemento.
    En este caso el primero y el de los procedimientos son de tipo lista, el resto objetos simples.

    Un código como este te parsea esos datos:

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    
    procedure TForm1.Button2Click(Sender: TObject);
    var
      i, j:integer;
      oProcList, OWList:TlkJSONlist;
      fName, str:String;
      obj: TlkJSONbase;
      oProc, objItem:TlkJSONobject;
      TS:TStrings;
    begin
      // Cargar el fichero usando TStrings (para obtener una cadena)
      fName := IncludeTrailingBackslash(ExtractFilePath(Application.ExeName));
      fName := FName + 'Datos.dat';
      // cargar el contenido del fichero
      TS := TStringList.CReate();
      try
        TS.LoadFromFile(fName);
        // A partir del JSON
        Str := TS.Text;
        // Eliminar saltos de carro (aunque no sería necesario, seguramente)
        Str := AnsiReplaceText(Str, sLineBreak, '');
        // Parsear.
        obj := TlkJSONbase.Create();
        // Crear el objeto raiz y cargar el contenido
        obj := TlkJSON.ParseText(Str);
        // El primer elemento ya es una array (empieza por [ )
        TlkJSONBase(OWList) := obj;
        // Recorrer la lista de elementos del array
        for i := 0 to (OWList.Count - 1) do begin
          // Objeto del array (paciente)
          TlkJSONBase(objItem) := OWList.Child[i];
          _Log('---------------- PACIENTE -----------------');
          _Log('id_muestra: ' + VarToStr(objItem.Field['id_muestra'].Value));
          _Log('Doc. paciente: ' + VarToStr(objItem.Field['paciente_documento'].Value));
     
          //...   resto de campos del paciente
     
         // llegamos a los procedimientos (es otro array)
         TlkJSONBase(oProcList) := objItem.Field['procedimientos'];
          // Parse de los procedimientos
          for j := 0 to (oProcList.Count - 1) do begin
            TlkJSONBase(oProc) := oProcList.Child[j];
            // Datos del procedinmiento
            _Log('---------------- PROC -------------------');
            _log('   Id: ' + VarToStr(oProc.Field['id'].Value));
            _log('   Código proc: ' + VarToStr(oProc.Field['procedimiento_codigo'].Value));
            _log('   CUPS: ' + VarToStr(oProc.Field['codigo_cups'].Value));
            _log('   Nombre: ' + VarToStr(oProc.Field['procedimiento_nombre'].Value));
          end;
          _Log(' ');
        end;
      finally
        FreeAndNil(TS);
      end;
    end;

    Y te dará una salida como esta:

    ########################################################
    —————- PACIENTE —————–
    id_muestra: 297
    Doc. paciente: 11223344
    —————- PROC ——————-
    Id: 257
    Código proc: A98546
    CUPS: B98546
    Nombre: Colesterol
    —————- PROC ——————-
    Id: 258
    Código proc: A4678
    CUPS: B4678
    Nombre: Triglicéridos

    —————- PACIENTE —————–
    id_muestra: 298
    Doc. paciente: 11335566
    —————- PROC ——————-
    Id: 259
    Código proc: A4678
    CUPS: B4678
    Nombre: GlucosaSanguínea
    ########################################################

    El procedimiento _Log lo único que hare es mostrar esa cadena en un memo.

    Un saludo.

  40. Tatty Fuentes
    lunes, 16 de enero de 2017 a las 13:51 | #40

    @Germán Estévez
    German… muchisimas, gracias… y no estaba tan perdida en mi analisis, de verdad te debo una.

  41. Boo06
    jueves, 19 de enero de 2017 a las 15:50 | #41

    Tengo una duda. A ver si algún “delphiniano” me la podría solventar. Entiendo que las llamadas que se realizan en el cliente 3 (componentes SOAP) son llamadas síncronas.

    Mi duda es, se pueden realizar llamadas con estos componentes de forma Asíncronas? Tendría que usar hilos? o hay algún parámetro de configuración a la hora de importar el wsdl donde pueda especificar el tipo de llamada y respuesta que quiero tener.

    O conocéis otros componentes para esto? En caso de que no sea configurable por el wizard.

    Un saludo y gracías por las respuestas.

  42. jueves, 19 de enero de 2017 a las 15:55 | #42

    @Boo06
    Puedes usar Threads al igual que se hace con llamadas utilizando las Indy.
    Crea un Thread con los componentes necesarios y realiza la llamada.

  43. Boo06
    jueves, 19 de enero de 2017 a las 16:07 | #43

    @Germán Estévez
    Así es, era la primera opción que tengo.
    Pero entiendo que es algo que no se puede configurar como en otros lenguajes?

    Muchas gracias por tu pronta respuesta @Germán Estévez

  44. jueves, 19 de enero de 2017 a las 16:59 | #44

    @Boo06
    Que yo sepa no.
    Debes usar Threads.

  45. Boo06
    viernes, 20 de enero de 2017 a las 21:37 | #45

    @Germán Estévez
    Y Delphi soporta de algún modo una petición ws-addressing?
    https://es.wikipedia.org/wiki/WS-Addressing
    Si no entiendo mal, consiste en consumir un servicio, indicándole que la respuesta se la de a la URL que se pasa en la cabecera. Dicha URL es otro servicio.

  46. Boo06
    domingo, 22 de enero de 2017 a las 10:06 | #46

    El método SOAP me devuelve el array vacio.

    DELPHI XE8 + SOAP… Sé que usas el xe5 para el ejemplo. Puede ser tema del importador de wsdl???

    RESPONSE

    ¿Alguna idea porque el array me lo devuelve en blanco?

    Y esta es la clase que me importa delphi
    // ************************************************************************ //
    // The types declared in this file were generated from data read from the
    // WSDL File described below:
    // WSDL : http://neftali.clubdelphi.com/agenda/listado2.php?wsdl
    // >Import : http://neftali.clubdelphi.com/agenda/listado2.php?wsdl>0
    // Encoding : ISO-8859-1
    // Version : 1.0
    // (22/01/2017 8:06:17 – – $Rev: 76228 $)
    // ************************************************************************ //

    unit listado2;

    interface

    uses Soap.InvokeRegistry, Soap.SOAPHTTPClient, System.Types, Soap.XSBuiltIns;

    const
    IS_UNQL = $0008;

    type

    // ************************************************************************ //
    // The following types, referred to in the WSDL document are not being represented
    // in this file. They are either aliases[@] of other types represented or were referred
    // to but never[!] declared in the document. The types from the latter category
    // typically map to predefined/known XML or Embarcadero types; however, they could also
    // indicate incorrect WSDL documents that failed to declare or import a schema type.
    // ************************************************************************ //
    // !:string – “http://www.w3.org/2001/XMLSchema”[Gbl]
    // !:integer – “http://www.w3.org/2001/XMLSchema”[Gbl]

    Estructura = class; { “http://neftali.clubdelphi.com”[GblCplx] }

    ArregloDeEstructuras = array of Estructura; { “http://neftali.clubdelphi.com”[GblCplx] }

    // ************************************************************************ //
    // XML : Estructura, global,
    // Namespace : http://neftali.clubdelphi.com
    // ************************************************************************ //
    Estructura = class(TRemotable)
    private
    FId: Int64;
    FNombre: string;
    FApellidos: string;
    FTelefono: string;
    FExtension: Int64;
    FDepartamento: string;
    FInterno: Int64;
    FEMail: string;
    published
    property Id: Int64 Index (IS_UNQL) read FId write FId;
    property Nombre: string Index (IS_UNQL) read FNombre write FNombre;
    property Apellidos: string Index (IS_UNQL) read FApellidos write FApellidos;
    property Telefono: string Index (IS_UNQL) read FTelefono write FTelefono;
    property Extension: Int64 Index (IS_UNQL) read FExtension write FExtension;
    property Departamento: string Index (IS_UNQL) read FDepartamento write FDepartamento;
    property Interno: Int64 Index (IS_UNQL) read FInterno write FInterno;
    property EMail: string Index (IS_UNQL) read FEMail write FEMail;
    end;

    // ************************************************************************ //
    // Name : Consulta de usuariosPortType
    // Namespace : http://neftali.clubdelphi.com
    // soapAction: http://neftali.clubdelphi.com/agenda/listado2.php/%operationName%
    // transport : http://schemas.xmlsoap.org/soap/http
    // style : rpc
    // use : literal
    // binding : Consulta de usuariosBinding
    // service : Consulta de usuarios
    // port : Consulta de usuariosPort
    // URL : http://neftali.clubdelphi.com/agenda/listado2.php
    // ************************************************************************ //
    Consulta_de_usuariosPortType = interface(IInvokable)
    [‘{653AB85B-2672-3FBF-90B8-F402B0B877C5}’]
    function ConsultaUsuario(const id: Int64): ArregloDeEstructuras; stdcall;
    function ConsultaUsuarios(const nom: string): ArregloDeEstructuras; stdcall;
    end;

    function GetConsulta_de_usuariosPortType(UseWSDL: Boolean=System.False; Addr: string=”; HTTPRIO: THTTPRIO = nil): Consulta_de_usuariosPortType;

    implementation
    uses System.SysUtils;

    function GetConsulta_de_usuariosPortType(UseWSDL: Boolean; Addr: string; HTTPRIO: THTTPRIO): Consulta_de_usuariosPortType;
    const
    defWSDL = ‘http://neftali.clubdelphi.com/agenda/listado2.php?wsdl’;
    defURL = ‘http://neftali.clubdelphi.com/agenda/listado2.php’;
    defSvc = ‘Consulta de usuarios’;
    defPrt = ‘Consulta de usuariosPort’;
    var
    RIO: THTTPRIO;
    begin
    Result := nil;
    if (Addr = ”) then
    begin
    if UseWSDL then
    Addr := defWSDL
    else
    Addr := defURL;
    end;
    if HTTPRIO = nil then
    RIO := THTTPRIO.Create(nil)
    else
    RIO := HTTPRIO;
    try
    Result := (RIO as Consulta_de_usuariosPortType);
    if UseWSDL then
    begin
    RIO.WSDLLocation := Addr;
    RIO.Service := defSvc;
    RIO.Port := defPrt;
    end else
    RIO.URL := Addr;
    finally
    if (Result = nil) and (HTTPRIO = nil) then
    RIO.Free;
    end;
    end;

    initialization
    { Consulta de usuariosPortType }
    InvRegistry.RegisterInterface(TypeInfo(Consulta_de_usuariosPortType), ‘http://neftali.clubdelphi.com’, ‘ISO-8859-1’, ”, ‘Consulta de usuariosPortType’);
    InvRegistry.RegisterDefaultSOAPAction(TypeInfo(Consulta_de_usuariosPortType), ‘http://neftali.clubdelphi.com/agenda/listado2.php/%operationName%’);
    InvRegistry.RegisterInvokeOptions(TypeInfo(Consulta_de_usuariosPortType), ioLiteral);
    { Consulta de usuariosPortType.ConsultaUsuario }
    InvRegistry.RegisterParamInfo(TypeInfo(Consulta_de_usuariosPortType), ‘ConsultaUsuario’, ‘return’, ”,
    ‘[Namespace=”http://neftali.clubdelphi.com”]’);
    { Consulta de usuariosPortType.ConsultaUsuarios }
    InvRegistry.RegisterParamInfo(TypeInfo(Consulta_de_usuariosPortType), ‘ConsultaUsuarios’, ‘return’, ”,
    ‘[Namespace=”http://neftali.clubdelphi.com”]’);
    RemClassRegistry.RegisterXSInfo(TypeInfo(ArregloDeEstructuras), ‘http://neftali.clubdelphi.com’, ‘ArregloDeEstructuras’);
    RemClassRegistry.RegisterXSClass(Estructura, ‘http://neftali.clubdelphi.com’, ‘Estructura’);

    end.

  47. Boo06
    martes, 31 de enero de 2017 a las 09:01 | #47

    Hola.
    NADIE que pueda ayudar?

    Sobretodo en la duda de los WS-ADDRESSING.

    Hay poca información sobre esto bajo Delphi. He podido ver que en versiones .net (Delphi 2007) había posibilidad http://edn.embarcadero.com/article/36962, pero para los XE, nada de información por no decir ninguna.

    Un saludo y gracias

  48. carol
    miércoles, 15 de marzo de 2017 a las 16:08 | #48

    Hola German!
    tengo una duda, en el ejercicio *Cliente Delphi6/Delphi XE5 + componentes SOAP*
    que estoy realizando en delphi 7, se me rompe en esta linea:
    mmo1.Lines.Add(‘Id: ‘ + IntToStr(info.Id));
    y efectovamente ya importe el wsdl y parametrice todo como corresponde, pero al ejecutarlo se quiebra allí… dice que hay una excepcion y el proceso tuvo que ser parado… que estará fallando :/, dame alguna pista para solucionar el problemilla.
    muchas gracias.

  49. miércoles, 15 de marzo de 2017 a las 16:15 | #49

    @carol
    Hola Carol.
    Es difícil saber de qué puede ser el error con tan poca información. Puede ser que el WSDL no esté generado correctamente (algunas versiones de Delphi no lo acababan de importar correctamente).

    ¿Estás haciendo ese ejemplo con Delphi 7? ¿Lo has importado con el Importer de Delphi7?
    ¿Puedes hacer la prueba con una versión más nueva?

  50. carol
    viernes, 24 de marzo de 2017 a las 22:41 | #50

    @Germán Estévez
    si lo puedo hacer con una versión mas nueva, efectivamente lo hago desde delphi 7 usando el importer.

Comment pages
1 2 3335
  1. Sin trackbacks aún.
What is 20 + 13 ?
Please leave these two fields as-is:
IMPORTANTE! Para continuar, debes contestar la pregunta anterior (para evitar SPAM) :-)