Saltar al contenido

Cliente SOAP en Delphi “El identificador está en el estado incorrecto para la operación solicitada”

Si hallas algún fallo con tu código o trabajo, recuerda probar siempre en un ambiente de testing antes subir el código al proyecto final.

Solución:

En un momento hubo una discusión sobre el error en una conversación ahora borrada hace mucho tiempo de los foros de Embarcadero, por Bruneau Babet, un miembro del personal de Embarcadero.

Bruno dijo:

Hola,

He publicado una versión parcheada de SOAPHTTPTrans.pas que contiene una solución para este problema aquí:

[forum link redacted, it didn’t work anymore anyways, the post is gone]

Aún puede anular el evento como se describe en la sección C ++ Builder referida; o, mucho más simple, al menos para los usuarios de Delphi, simplemente agregue el SOAPHTTPTrans.pas actualizado al proyecto de su aplicación. Háganos saber si eso no funciona para usted.

Salud,

Bruneau

Puede obtener la reparación y las notas al respecto en su formato de foro original desde el siguiente enlace de pastebin y en bitbucket para que no tenga que extraer el archivo del texto circundante.

Actualización de Warren 2016: Alguien que intentó usar la solución en Delphi XE me informó que esta solución NO funciona para ellos en Delphi XE. Se agradecería cualquier actualización adicional del código en bitbucket que resuelva los errores restantes.

Me encontré con el El identificador está en un estado incorrecto para la operación solicitada en noviembre de 2018 usando Delphi Tokyo 10.2.3, luego miró el parche de código en el enlace de pastebin debajo de la respuesta de Arjen.

Ese código es muy antiguo y el código de prueba ya no funciona (el servicio SOAP no está disponible). Además, no está claro en el código de Bruneau. qué parcheó exactamente.

Comparando esa fuente y la de mi versión de Delphi, parece que estas son las (dos) modificaciones requeridas en el HandleWinInetError procedimiento (‘PARCHE AQUÍ’):

function THTTPReqResp.HandleWinInetError(LastError: DWord; 
                                         Request: HINTERNET;
                                         RaiseError: Boolean): DWord;

  function CallInternetErrorDlg: DWord;
  var
    P: Pointer;
  begin
    Result := InternetErrorDlg(GetDesktopWindow(), Request, LastError,
                               FLAGS_ERROR_UI_FILTER_FOR_ERRORS or
                               FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS or
                               FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, P);

     After selecting client certificate send request again,
      Note: InternetErrorDlg always returns ERROR_SUCCESS when called with
            ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED 
    if LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED then
      Result := ERROR_INTERNET_FORCE_RETRY;
  end;

const
   Missing from our WinInet currently 
  INTERNET_OPTION_CLIENT_CERT_CONTEXT = 84;

var
  Flags, FlagsLen, DWCert, DWCertLen: DWord;
  ClientCertInfo: IClientCertInfo;
  CertSerialNum: string;
$IFDEF CLIENT_CERTIFICATE_SUPPORT
  hStore: HCERTSTORE;
  CertContext: PCERT_CONTEXT;
$ENDIF
begin
   Dispatch to custom handler, if there's one 
  if Assigned(FOnWinInetError) then
    Result := FOnWinInetError(LastError, Request)
  else
  begin
    Result := ERROR_INTERNET_FORCE_RETRY;
     Handle INVALID_CA discreetly 
    if (LastError = ERROR_INTERNET_INVALID_CA) and (soIgnoreInvalidCerts in InvokeOptions) then
    begin
      FlagsLen := SizeOf(Flags);
      InternetQueryOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(@Flags), FlagsLen);
      Flags := Flags or SECURITY_FLAG_IGNORE_UNKNOWN_CA;
      InternetSetOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(@Flags), FlagsLen);
    end
    else if (LastError = ERROR_INTERNET_SEC_CERT_REV_FAILED) and (soIgnoreInvalidCerts in InvokeOptions) then
    begin
      FlagsLen := SizeOf(Flags);
      InternetQueryOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(@Flags), FlagsLen);
      Flags := Flags or SECURITY_FLAG_IGNORE_REVOCATION;
      InternetSetOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(@Flags), FlagsLen);
    end
$IFDEF CLIENT_CERTIFICATE_SUPPORT
    else if (LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) and
             Supports(Self, IClientCertInfo, ClientCertInfo) and
             (ClientCertInfo.GetCertSerialNumber <> '') then
    begin
      CertSerialNum := ClientCertInfo.GetCertSerialNumber();
      hStore := ClientCertInfo.GetCertStore();
      if hStore = nil then
      begin
        hStore := CertOpenSystemStore(0, PChar('MY'));
        ClientCertInfo.SetCertStore(hStore);
      end;
      CertContext := FindCertWithSerialNumber(hStore, CertSerialNum);
      if CertContext <> nil then
      begin
        ClientCertInfo.SetCertContext(CertContext);
        InternetSetOption(Request, INTERNET_OPTION_CLIENT_CERT_CONTEXT,
                          CertContext, SizeOf(CERT_CONTEXT));
      end
      else
      begin
        if RaiseError then RaiseCheck(LastError);  // PATCH HERE
        Result := CallInternetErrorDlg;
      end;
    end
$ENDIF
    else if (LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) and (soPickFirstClientCertificate in InvokeOptions) then
    begin
       This instructs WinInet to pick the first (a random?) client cerficate 
      DWCertLen := SizeOf(DWCert);
      DWCert := 0;
      InternetSetOption(Request, INTERNET_OPTION_SECURITY_SELECT_CLIENT_CERT,
                        Pointer(@DWCert), DWCertLen);
    end
    else
    begin
      if RaiseError then RaiseCheck(LastError);  // PATCH HERE
      Result := CallInternetErrorDlg;
    end;
  end;
end;

Tenga en cuenta que el RaiseError El parámetro de procedimiento ni siquiera se usó antes de este parche 😉

Aquí hay un código de prueba que utiliza el servicio SOAP del servicio web SOAP de la base de datos nacional de pronóstico digital (NDFD) de la NOAA:

Uses SOAP.SOAPHTTPTrans;

const Request2 =
'' +
'   ' +
'   ' +
'      ' +
'         38.9936' +
'         -77.0224' +
'         %tomorrow%' +
'         5' +
'         e' +
'         12 hourly' +
'      ' +
'   ' +
'';

const URL2= 'https://graphical.weather.gov:443/xml/SOAP_server/ndfdXMLserver.php';

procedure TFrmHandleWinINetError.Button1Click(Sender: TObject);
var
  RR: THTTPReqResp;
  Response: TMemoryStream;
  U8: UTF8String;
begin
  RR := THTTPReqResp.Create(nil);
  try
    try
      RR.URL := URL2;
      RR.UseUTF8InHeader := True;
      RR.SoapAction := 'NDFDgenByDay';
      Response := TMemoryStream.Create;
      RR.Execute(Request2, Response);
      SetLength(U8, Response.Size);
      Response.Position := 0;
      Response.Read(U8[1], Length(U8));
      ShowMessage(String(U8));
      except
        on E:Exception do ShowMessage('ERROR CAUGHT: ' + e.message);
      end;
    finally
      Response.Free;
      RR.Free;
    end;
  end;
end;  

Sin se detectan los errores de parche en el extremo final de la URL, pero los errores en el nombre de dominio solo desencadenan un mensaje de error vacío.
Con el parche esos también están atrapados.

He informado del problema en RAD Studio Quality Portal con el número RSP-21862

Úselo bajo su propio riesgo e informe cualquier hallazgo adicional.


Adición: El problema se solucionó en diciembre de 2018 en Delphi 10.3 Rio y el problema del Portal de calidad se cerró con la siguiente observación:

En RAD Studio 10.3, la implementación de THTTPReqResp se cambió y se reemplazó por THTTPClient. Entonces, este problema ya no se aplica.

No he verificado esto.

Sección de Reseñas y Valoraciones

Eres capaz de ayudar nuestra ocupación poniendo un comentario y valorándolo te damos la bienvenida.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *