(3/5) Generación de un cliente (WebService) en Delphi.
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.
Contenido
“ROADMAP” DE LAS ENTRADAS
- (1/5) Introducción al problema y solución propuesta.
- (2/5) Generación del Webservice en PHP.
- (3/5) Generación de un cliente de Escritorio en Delphi.
- (4/5) Generación del cliente iOS/Android (código compartido)
- (5/5) Publicación en AppStore (Google Play) paso a paso.
Siguiendo con nuestra “hoja de ruta”, vamos a desarrollar varios clientes de escritorio en Windows, utilizando diferentes alternativas y posibilidades de las que disponemos.
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.
En 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.
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.
// 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; |
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).
El procedimiento ParseResponse no tienen ningún secreto, y es el que tenéis a continuación.
// 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.
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.
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:
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:
procedure SearchUsuarios(AId:integer); overload; procedure SearchUsuarios(ANombre:string); overload; |
Cuya implementación es muy básica:
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).
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)
Como 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. 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.
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.
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.
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.
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):
- Cliente Delphi 6 + Indy + LkJSON
- Cliente Delphi XE5 + componentes REST
- Cliente Delphi6/Delphi XE5 + componentes SOAP
- Fichero PHP con el servidor web (primera versión; listado.php)
- Fichero PHP con el servidor web (segunda versión)
- (Añadidos) Ficheros Extra; Pruebas y variantes.
Pues hasta aquí hemos llegado hoy.
Hasta la próxima.
Embarcadero MVP.
Analista y Programador de Sistemas Informáticos.
Estudios de Informática (Ingeniería Técnica Superior) en la UPC (Universidad Politécnica de Barcelona).
Llevo utilizando Delphi desde su versión 3. Especialista en diseño de componentes, Bases de Datos, Frameworks de Persistencia, Integración Continua, Desarrollo móvil,…
hola, compañero tengo una trama Json en archivo *.txt* y nececito parcearla.
el método principal lo tengo así y funciona:
procedure TForm1.trama;
var
fRespuesta: String;
obj: TlkJSONbase;
v_ : Integer;
begin
mmo1.Lines.LoadFromFile(‘D:\SL_SOFTWARE\LaboratorioBACKUP_DBLAB_HRD25_1.DL3\leerJSON\JSON_ORDEN.txt’);
fRespuesta:= mmo1.Lines.Text;
obj:= TlkJSONbase.Create();
try
mmo1.Clear;
v_:=0;
obj:= TlkJSON.ParseText(fRespuesta);
mmo1.Lines.Text:= GenerateReadableText(obj, v_);
datosEnTabla(TlkJSONobject(obj));
finally
FreeAndNil(obj);
end;
end;
la trama que necesito parsear en tablas temporales son:
{
«cabeza1»: {
«tabla1»: {
«campo»: «0»,
«campo»: «1»
},
«tabla2»: {
«campo»: «xxxxxx»,
«campo»: «xxxxxx»,
«campo»: «xxxxxxx»
},
«tabla3»: {
«campo»: «L-xxxxxxxxxxxx»
},
«tabla4»: {
«tabla4.1»: {
«campo»: «xxxxxxxxxxxx»,
«campo»: «xxxxx»,
«campo»: «x»,
«campo»: «»
}
}
}
para un solo registro debo interpretar dicha trama, ahora bien, en la linea
**datosEnTabla(TlkJSONobject(obj));** la ejecucion se revienta.
y el codigo que tengo en dicha funcion es:
procedure TForm1.datosEnTabla(AObjResp: TlkJSONobject);
var
objDatosList:TlkJSONlist;
objDatos, objtmp:TlkJSONobject;
objBase: TlkJSONbase;
i,j,k,l:integer;
Str:String;
cds1,cds2,cds3,cds4:TClientDataSet;
begin
// Limpiar el contenido
cds1 := cd1;
cds1.EmptyDataSet;
cds2:= cd2;
cds2.EmptyDataSet;
cds3:= cd3;
cds3.EmptyDataSet;
cds4:= cd4;
cds4.EmptyDataSet;
// Objeto usuarios?
*** if (AObjResp.IndexOfName(‘DocumentElement’) -1) then ***
begin
//(AObjResp.IndexOfName(‘DatosGenerales’) -1)
// Lo almaceamos en un tipo «lista»
TlkJSONBase(objDatosList) := AObjResp.Field[‘DocumentElement’];
// Recorrer la lista de usuarios.
for i := 0 to (objDatosList.Count – 1) do begin
// Objeto usuario
TlkJSONBase(objDatos) := objDatosList.Child[i];
TlkJSONBase(objtmp) := objDatos.FieldByIndex[0];
// Añadimos un registro al CDS
cds1.Append;
try
// Recoger los campos del objeto
cds1.FieldByName(‘xxxx’).AsString := VarToStr(objtmp.Field[‘xxx’].Value);
cds1.FieldByName(‘xxxxx’).AsString := VarToStr(objtmp.Field[‘xxx’].Value);
cds1.FieldByName(‘xx’).AsString := VarToStr(objtmp.Field[‘xxx’].Value);
cds1.FieldByName(‘xxx’).AsString := VarToStr(objtmp.Field[‘xxx’].Value);
// mostrar
cds1.Open;
finally
cds1.Cancel;
end;
end;
//TlkJSONBase(objDatosList) := AObjResp.Field[‘DatosPaciente’];
for j := 0 to (objDatosList.Count – 1) do begin
// Objeto usuario
TlkJSONBase(objDatos) := objDatosList.Child[j];
TlkJSONBase(objtmp) := objDatos.FieldByIndex[0];
// Añadimos un registro al CDS
cds2.Append;
try
// Recoger los campos del objeto
cds2.FieldByName(‘xxx’).AsString := VarToStr(objtmp.Field[‘xxx’].Value);
cds2.FieldByName(‘xxx’).AsString := VarToStr(objtmp.Field[‘xxx’].Value);
cds2.FieldByName(‘xxx’).AsString := VarToStr(objtmp.Field[‘xx’].Value);
cds2.FieldByName(‘xxx’).AsString := VarToStr(objtmp.Field[‘xx’].Value);
// mostrar
cds2.Open;
finally
cds2.Cancel;
end;
end;
//TlkJSONBase(objDatosList) := AObjResp.Field[‘DatosOrden’];
for k := 0 to (objDatosList.Count – 1) do begin
// Objeto usuario
TlkJSONBase(objDatos) := objDatosList.Child[k];
TlkJSONBase(objtmp) := objDatos.FieldByIndex[0];
// Añadimos un registro al CDS
cds3.Append;
try
// Recoger los campos del objeto
cds3.FieldByName(‘xxxxx’).AsString := VarToStr(objtmp.Field[‘xxxx’].Value);
// mostrar
cds3.Open;
finally
cds3.Cancel;
end;
end;
//TlkJSONBase(objDatosList) := AObjResp.Field[‘Examen’];
for l := 0 to (objDatosList.Count – 1) do begin
// Objeto usuario
TlkJSONBase(objDatos) := objDatosList.Child[l];
TlkJSONBase(objtmp) := objDatos.FieldByIndex[0];
// Añadimos un registro al CDS
cds4.Append;
try
// Recoger los campos del objeto
cds4.FieldByName(‘xx’).AsString := VarToStr(objtmp.Field[‘xx’].Value);
cds4.FieldByName(‘xx’).AsString := VarToStr(objtmp.Field[‘xx’].Value);
cds4.FieldByName(‘xx’).AsString := VarToStr(objtmp.Field[‘xx’].Value);
cds4.FieldByName(‘xx’).AsString := VarToStr(objtmp.Field[‘xx’].Value);
cds4.FieldByName(‘xx’).AsString := VarToStr(objtmp.Field[‘xx’].Value);
// mostrar
cds4.Open;
finally
cds4.Cancel;
end;
end;
end;
end;
en la linea demarcada por los 3 asteriscos me arroja el error de inmediato… que puedo estar aplicando mal?
GRACIAS POR LA AYUDA.
vale aclarar que cabeza1=documentElement, tabla1=DatosGenerales, tabla2=DatosPaciente, tabla3=DatosOrden, tabla4=Examenes, tabla4.1=Examen.
Como se puede implementar la insercion-actualizacion desde el cliente (CRUD)? Alguna sugerencia ?
@jibleg
Hola.
En el ejemplo se lanzan consultas. De la misma forma se pueden lanzar optras sentencias (INSERT/UPDATE/DELETE).
Hola.
Saludos.
Felicidades por los aportes, nomas tengo la duda de como se puede enviar el resultado de un Query a una base de datos Firebird desde Delphi a un sitio WEB.
Gracias de antemano.
@Antonio
Hola.
Todo dependerá de cómo esté hecha la web.
Si la web tiene un WebService, como en el ejemplo, te deberás conectar con los mismos componentes que uso yo aquí y enviar ese reultado en formato JSON o XML (eso depende de lo que implemente la web).
Hola, saludos.
En El Cliente1 con Delphi6 me sale este error al cambiar de servidor: HTTP/1.1 403 Forbidden
@Luis
Hola Luis.
Yo acabo de probar en ejecutable que se adjunta al proyecto y me funciona correctamente. También he compilado el proyecto desde Delphi y funciona correctamente.
¿Es posible que tengas bloqueado el acceso por el Antivirus o el cortafuegos?
¿Puedes acceder a esta web correctamente y ver los resultados (http://neftali.clubdelphi.com/agenda/listado.php)?
@Germán Estévez
Hola Germán
Estoy realizando el ejercicio Cliente Delphi6/Delphi XE5 + componentes SOAP
pero con delphi7
Pero me sale esta excepcion
—————————
Debugger Exception Notification
—————————
Project Project1.exe raised exception class ERemotableException with message ‘Access violation at address 004F1DBD in module ‘ServicioXml.dll’. Read of address FFFFFFFF’. Process stopped. Use Step or Run to continue.
—————————
OK Help
—————————
y hasta ahi llega.
Creo una funcion sencilla que me retorne un string y si me funciona, pero no con
function Orden(const pId: WideString; const pTipoId: WideString): ArregloDeEstructuras; stdcall;
no se que pueda ser
Gracias pro la ayuda que me puedas prestar
@carol
Buen dia carol
pudiste solucionar el problema, ando bardo por lo mismo
—————————
Debugger Exception Notification
—————————
Project Project1.exe raised exception class ERemotableException with message ‘Access violation at address 004F1DBD in module ‘ServicioXml.dll’. Read of address FFFFFFFF’. Process stopped. Use Step or Run to continue.
—————————
OK Help
—————————
@alberto
Hola Alberto.
Tendrías que ejecutar el programa pasa a paso y ver qué línea y porqué te está generando la excepción.
Yo he vuelto a probar ele ejemplo (aunque lo he tenido que compilar con una versión más nueva) y no falla.
No devuelve datos, porque el WebService del ejemplo está parado, pero en ningún caso da error.
El error que comentas es un error de memoria. Porque estás accediendo a algun elemento que no se ha creado o que ya está destruído -o en general accediendo a un espacio de memoria «sucio»-.
Revisa qué haces en ese caso.
Un saludo.
@Germán Estévez
Buen día German, hice lo que me sugieres, y es cierto.
El problema es en la lógica que aplica en la función. Pero no se que este haciendo bien.
Espero me pueda colaborar.
Tengo esta función
function TServicioSOAPXml.Orden(pId,
pTipoId: WideString): ArregloDeEstructuras;
var
EOrden:Estructura;
Arreglo : ArregloDeEstructuras;
begin
EOrden.Create;
EOrden.NumeroOrden := 1;
EOrden.Fecha := ’01/01/2020′;
EOrden.Medico := ‘Jmarin’;
EOrden.UndidadFuncional := 10;
EOrden.CodigoCum := ‘890201’;
EOrden.CantidadEntregada := 2;
EOrden.NombreMedicamento := ‘Acetaminofem’;
Arreglo[0]:= EOrden;
Result := Arreglo;
end;
solo le he dejado el EOrden.Create; y me sale el error.
No se que este haciendo mal.
Ojala me puedas dar una luz
De antemano agradezco tu colaboración
@alberto
Es que parece que
EOrden.Create;
está mal construido.
No debería ser algo como esto o similar:
EOrden := Estructura.Create;
@Germán Estévez
Gracias german
Leyendo un foro (http://delphiaccess.com/foros/index.php/topic/6114-webservice-y-tremotable/) hice lo siguiente y funciono
SetLength(Arreglo,1);
Arreglo[0]:= Estructura.create;
Arreglo[0].NumeroOrden := 1;
Arreglo[0].Fecha := ’01/01/2020′;
Arreglo[0].Medico := ‘Jmarin’;
Arreglo[0].UndidadFuncional := 10;
Arreglo[0].CodigoCum := ‘890201’;
Arreglo[0].CantidadEntregada := 2;
Arreglo[0].NombreMedicamento := ‘Acetaminofem’;
Result := Arreglo;
Agradezco tu ayuda y lo pronto de tu respuesta.
@alberto
Hola Alberto.
Esa sí parece la forma correcta de crearlo.
Un saludo.
Hola tengo una url que me entregaron para registrar datos, supuestamente , como le paso los datos para insertarlos en base de datos , me entregaaron una url nada mas .
@HELBERT YESID GARCIA RODRIGUEZ
Hola Helbert.
Deben decirte además de la URL, el formato de envío y de recepción de los datos, junto con las operaciones disponibles del WebService (POST, PUT, GET,…).
Esa documentación debe decirte si los datos deben enviarse como parámetros y dentro del cuerpo en formato JSON, etc, etc,…
Gracias ,ya me los dieron , los pruebo con la herramienta Rest Debugger
En el Post :
Post (https://poti.uno/apiRest/public/api/paciente…………)
En el Content Type (coloco) :
application/json
En el Custom body (paso el json):
{«documento»:»777777″,»nombres»:»Helbert Garcia»}
Me funciona ya desde ahí ( Rest Debugger)
No lo he podido enviar desde delphi (10.2)
Gracias
@HELBERT YESID GARCIA RODRIGUEZ
Dependiendo de la versión de RESTDebugger que tengas, a la derecha hay un botón de [Copy Components] con el que generas y puedes copiar los componentes necesarios en tu aplicación delphi, con la misma configuración del REStDebugger para que funcione.
Tengo la Versión XE7 y tratando de utilizar la herramienta REST Debugger accediendo a un recurso a una api me manda el siguiente : error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure
la cual según la documentación del proveedor del servicio API este es un ejemplo para conectar:
«https://app.interfuerza.com/api/»,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => «»,
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => «GET»,
CURLOPT_POSTFIELDS =>»{\r\n \»class\»:\»GET\»,\r\n \»action\»:\»customers\»,\r\n \»page\»:\»1\»\r\n}»,
CURLOPT_HTTPHEADER => array(
«X-IFX-Token: f22db1478793fxxxxxxxxxxxdddddddzzzz»,
«Content-Type: application/json»,
«Cookie: __cfduid=d8d282f22fc0c618e661ae8f1c8ce24991591750636»
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
este es el ejemplo que me enviaron aunque ya realicé la preuba usando postman fue satisfactoria la conexion mas no con REST Debugger de Embarcadero
@Erick Castillo
Tal vez deberías probar con una versión más nueva de RESTDebugger.
Parece que tienes algun problema con la versión de ssl.
@Germán Estévez
Tienes mucha razón ya descargué la versión la cual ya no envió el error SSL3_READ_BYTES:sslv3 antes mencionado.
Encontré el problema con la versión Openssl la cual indy 10.6.0.5169 no soporta openssl1.1.1u ni TLS 1.3 ya está solucionado con la adquisición de Delphi 10.4 Sydney.
Agradezco tu colaboración
Saludos;
Não estou a conseguir usar o teu exemplo CLIENTE 1: Delphi6 + Indy + LkJSON, quando pressiono o botao «Obterner resultado» dá erro Atenção : Intercept value is not valid.
Podes ajudar a ultrapassar este erro por favor.
Cumprimentos
Rui Gomes
@Rui Gomes
Hola Rui.
El ejemplo inicial utilizaba la web http:// ahora la web ha cambiado a https://
Hay que añadir un componente de IOHandler para poder conectar vía SSL.
He actualizado el ejemplo de la web.
Olá Germán.
Podes enviar o link exemplo actualizado por favor.
Fiz download de http://neftali.clubdelphi.com/agenda/files/cliente1_webservice.zip mas ainda está na versão antiga.
Cumprimentos
Rui Gomes
@Rui Gomes
Hola Rui.
La versión que hay aquí:
http://neftali.clubdelphi.com/agenda/files/cliente1_webservice.zip
Ya está actualizada.
Incluye las DLLs para acceder a webs https:// (SSL).
Además si comparas el código con la versión anterior varás que hay algunas líneas diferentes (donde se crea un componente TIdSSLIOHandlerSocket).
De forma alternativa, también lo he subido aquí: https://gofile.io/d/wa9jg6
Olá Germán
Muito obrigado por o exemplo.
Como posso fazer uma autenticação port post utilizando json em delphi6, tens algum exemplo que eu possa utilizar?
{
«login»: «sa»,
«password»: «Mercury»,
«idioma»: «pt-pt»,
«server»: «dell-rgomes\\eticadata»,
«sistema»: «sisTema»
}
@Rui Gomes
Dependiendo de cómo admita la autentificación el WS. Puedes enviarlos como parámetros de la cabecera.
Eso debe estar en la documentación.
@Germán Estévez
Hola Germán.
Estoy probando el código pero me da el error de no encontrar IdIOHandler.dcu.
Supongo que luego me dará error por no encontrar los otros, por favor me puede comentar como encontrar esos componentes para que me funcione el ejemplo, muchas gracias, saludos.
@Sofia
Hola Sofía.
Dime cual de los proyectos te está dando error (de los que hay más arriba) y con qué versión de delphi lo estás compilando.
@Germán Estévez
Hola, gracias por responder.
Estoy probando con este:
http://neftali.clubdelphi.com/agenda/files/cliente1_webservice.zip
Ya que es el que dice contener SSL que es con lo que he tenido más dificultades.
Muchas gracias.
@Germán Estévez
Hola
Estoy probando con este código:
http://neftali.clubdelphi.com/agenda/files/cliente1_webservice.zip
En delphi 6
@Sofia
Hola Sofía.
Esa unit pertenece a las Indy.
No he podido probarlo con Delphi6, pero con delphi7 funciona perfectamente.
La unit debería estar en:
c:\Program Files (x86)\Borland\Delphi6\Source\Indy\
Si la encuentras ahí, prueba a añadir ese path de búsqueda a las opciones el proyecto.
en cooncreto esa unit creo que puedes eliminarla, pero te pasará algo similar con las otras de las Indy (IdSSLOpenSSL, IdIOHandlerSocket).
@Germán Estévez
Si elimino las unit no me funciona y necesito hacerlo funcionar con SSL en delphi 6, es el problema que he estado tratando de resolver y esas unit no están en la carpeta Indy de delphi 6.
No sé si alguien lo ha podido probar con Delphi 6.
Gracias
@Sofia
En ese caso, es posible que sea problema de las Indy que vienen con la versión de Delphi6.
Acabo de ver que la versión 8 de las Indy no tra esa unit, en cambio la versión 9 ya si la trae.
Puedes descargar las diferentes verisones aquí:
https://www.indyproject.org/download/
Revisando los ZIPs, veo que en la 9 ya viene incluído.
Podrías intentar actualizar el paquete de las Indy a la versión 9 (o a la 10), que seguramente debía ser el que tenía yo instalado cuando desarrollé el ejemplo.
Hola Neftali.
Gracias por tu gran aporte.
Trate de probar tu ejemplo «CLIENTE 3 (Delphi XE5 + Componentes SOAP)». pero al intentar compilar, presenta el siguiente error:
[dcc32 Fatal Error] E2202 Required package ‘BOOT’ not found. Alguna sugerencia?
Estoy probando con delphi Rio 10.3.
Gracias.
Saludos.
Jose Miguel.
@jose miguel
Hola jose miguel, a mi me pasaba lo mismo mi solución fue:
Quitar los paquetes que no encuentra. Esto se ahce desde en Project/Options/Packages/Runtime Packages/Value from «All configurations-All platforms»
[dcc32 Fatal Error] E2202 Required package ‘BOOT’ not found
[dcc32 Fatal Error] E2202 Required package ‘Formu_1’ not found
[dcc32 Fatal Error] E2202 Required package ‘bdertl’ not found
…
No se si esto es correcto, pero logré que el programa ejecutara
El artículo super interesante, me regresó la pasión por Delphi, ya tenia años que no le metía mano, pero no he logrado que funcione del todo el CLIENTE 3 (Delphi XE5 + Componentes SOAP). No he podido lograr que deje de salir el mensaje «El documento XML debe tener un elemento de nivel superior. Line 0», esto ocurre solamente al al ponerle un valor al Id: al darle click al botón «Consulta». Alguien que tenga un idea de que estar sucediendo. Estoy usando Delphi 10.1
@Alberto Osuna
Hola Alberto.
El artículo es antiguo y siento decirte que los ficheros de prueba en el servidor ya no están activos.
Lo más sencillo es que hagas las pruebas sobre un servidor local, por ejemplo con XAMP.
El ejemplo debe funcionar sin problemas, pero no se puede probar en línea.
Un saludo.
Buenas tardes Germán:
Hace tiempo que no charlamos.
La verdad es que con los servicios web no habia probado nada y ahora que lo intento no tengo claro como funciona.
Estoy probando con uno de Aduanas que está en modo de pruebas.
Hay que enviarle un archivo XML y devuelve otro XML
He puesto el componente.
La propiedad WDSLLocation = https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aduanas/es/aeat/adex/jdit/ws/aes/CC515CV1.wsdl
Y automaticamente me sale la lista de Port (CC515CV1) y Service (CC515CV1Service).
En el certificado vale cualquiera válido, yo pongo el mio personal.
Lo que no veo es que código poner para enviarle el archivo y como recibirlo.
Si pudieras ayudarme te estaría muy agradecido.
Un saludo
@Antonio
Hola Antonio.
Pedona por no responder antes.
Habitualmente siempre lo hago, pero se me había quedado «enterrado» en cientos de comentarios SPAM.
Si revisas el fichero generado a partir de la importación del WSDL, junto a la implementación está la función que te sirve de inicio. En este caso:
function GetCC515CV1(UseWSDL: Boolean=System.False; Addr: string=»; HTTPRIO: THTTPRIO = nil): CC515CV1;
A partir de ahí revisa los tipos de las estructuras que usa como parámetros.
Un saludo.