2009-07-09 19 views
6

He escrito un pequeño servicio web sencillo de C#, alojado desde un EXE independiente a través de WCF. El código - algo simplificado - se ve así:¿Cómo consumir el servicio web no alojado en IIS, WCF y C# de Delphi 2007?

namespace VMProvisionEXE 
{ 
class EXEWrapper 
{ 
    static void Main(string[] args) 
    { 
     WSHttpBinding myBinding = new WSHttpBinding(); 
     myBinding.Security.Mode = SecurityMode.None; 

     Uri baseAddress = new Uri("http://bernard3:8000/VMWareProvisioning/Service"); 
     ServiceHost selfHost = new ServiceHost(typeof(VMPService), baseAddress); 

     try 
     { 
      selfHost.AddServiceEndpoint(typeof(IVMProvisionCore), myBinding, "CoreServices"); 

      ServiceMetadataBehavior smb = new ServiceMetadataBehavior(); 
      smb.HttpGetEnabled = true; 
      smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy12; 
      selfHost.Description.Behaviors.Add(smb); 

      // Add MEX endpoint 
      selfHost.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding(), "mex"); 

      selfHost.Open(); 
      Console.WriteLine("The service is ready."); 
      Console.ReadLine(); 

El resto del código C#; la clase VMPService anterior implementa VMProvisionCore.IVMProvisionCore.

Puedo crear fácilmente una aplicación cliente Visual Studio 2008 que consuma este servicio. No hay problemas. Pero usar Delphi 2007 es un problema diferente. Puedo usar el importador WSDL en Delphi para recuperar el WSDL de (en este caso) http://bernard3:8000/VMWareProvisioning/Service?wsdl La unidad de importación se compila muy bien. Tengo para inicializar el proxy manualmente desde el WSDL no contiene una dirección URL (observe que el extra "/ CoreServices" como se muestra en el código C#):

var 
    Auth: AuthenticateUser; 
    AuthResponse: AuthenticateUserResponse; 
    CoreI: IVMProvisionCore; 
begin 
    CoreI:= GetIVMProvisionCore(False, 'http://bernard3:8000/VMWareProvisioning/Service/CoreServices'); 
    Auth:= AuthenticateUser.Create; 
    try 
    Auth.username:= 'test'; 
    Auth.password:= 'test'; 
    AuthResponse:= CoreI.AuthenticateUser(Auth); 
    finally 
    FreeAndNIL(Auth); 
    end; 

El código anterior generará un error cuando se golpea el "CoreI.AuthenticateUser (Auth);". El error es "no puede procesar el mensaje porque el tipo de contenido 'text/xml; charset = "UTF-8" no era el esperado tipo' application/soap + xml;. Charset = UTF-8"

I sospecho que tengo un pequeño error estúpido en alguna parte, tal vez durante la importación del WSDL o en las opciones de conexión o algo así. ¿Alguien puede ayudar?

Respuesta

4

Encontré la solución. Es múltiple y requiere algunos cambios en el lado C#, más en el lado Delphi. Tenga en cuenta que esto se probó con Delphi 2007 y Visual Studio 2008.

C# side: Utilice BasicHttpBinding en lugar de WSHttpBinding.

Fix Paso 1

BasicHttpBinding myBinding = new BasicHttpBinding(); 
myBinding.Security.Mode = BasicHttpSecurityMode.None; 

Este cambio va a resolver los errores de aplicación/jabón + xml en el lado Delphi.

Delphi 2007 lado: Correr contra el servicio web modificado C# ahora generará errores como este:

Exception class ERemotableException with message 'The message with Action '' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).'

Para resolver este problema, agregue SOAPActions a todas sus interfaces compatibles.Aquí está el ejemplo de mi código; esto debe hacerse después de que todos los cambios InvRegistry realizadas por la sección de inicialización del import-de-WSDL-PAS-archivo:

Fix Paso 2

InvRegistry.RegisterDefaultSOAPAction(TypeInfo(IVMProvisionCore), 'http://Cisco.VMProvision.Core/CoreServices/%operationName%'); 

El nombre del tipo y la dirección URL debe ser obtenida a partir Delphi generó un archivo de importación desde el WSDL y/o una inspección del WSDL real. El ejemplo anterior fue para mi propio proyecto. Después de estos cambios en el código, entonces te el error:

Exception class ERemotableException with message 'The formatter threw an exception while trying to deserialize the message: Error in deserializing body of request message for operation....

Este error se resuelve añadiendo el siguiente código (créditos a http://www.bobswart.nl/weblog/Blog.aspx?RootId=5:798). De nuevo, este nuevo código debe ser después de todo el material InvRegistry en la inicialización del archivo WSDL a PAS.

Fix Paso 3

InvRegistry.RegisterInvokeOptions(TypeInfo(IVMProvisionCore), ioDocument); 

En este punto, los paquetes ir y venir entre Delphi y C# - pero los parámetros no funcionarán correctamente. El C# recibirá todos los parámetros como nulos y Delphi no parece estar recibiendo los parámetros de respuesta correctamente. El último paso del código es usar un objeto THTTPRIO ligeramente personalizado que permita los parámetros literales. El truco de esta parte es asegurarse de que la opción se aplique DESPUÉS de que se haya obtenido la interfaz; hacerlo antes no funcionará. Aquí está el código de mi ejemplo (solo fragmentos).

Fix Paso 4

var 
    R: THTTPRIO; 
    C: IVMProvisionCore; 
begin 
    R:= THTTPRIO.Create(NIL); 
    C:= GetIVMProvisionCore(False, TheURL, R); 
    R.Converter.Options:= R.Converter.Options + [soLiteralParams]; 

Y ahora - mi aplicación Delphi 2007 puede hablar con el C#, independiente, no IIS, servicio web WCF!

0

También he tenido el mismo problema al consumir el servicio web C# en delphi.
Delphi 7.0/2005/2007 no es compatible con las nuevas definiciones WSDL.
Para esto, deberá descargar el último importador de WSDL (WSDLImp.exe). También proporcionará el código fuente para los archivos actualizados del código fuente delphi pass.

+0

¿Sabes dónde puedo descargar los últimos WSDLImp.exe? Revisé el sitio web de Embarcadero, pero no pude encontrar nada más allá de las referencias a errores corregidos. –

1

Esto se debe a una discrepancia en la versión de SOAP. El servicio C# espera un mensaje SOAP12 y recibe un mensaje SOAP11 desde su aplicación Delphi. Dependiendo de su situación, debe cambiar cualquiera de los dos lados. Realmente no puedo comentar sobre el lado Delphi. En el lado de WCF, puede usar BasicHttpBinding, que por defecto es SOAP11 o, si necesita más control, usar una CustomBinding que especifique un tipo de mensaje de SOAP11.

+0

Al cambiar el servidor de C# a BasicHttpBinding, se solucionó el problema de la aplicación/soap + xml. Sin embargo, abrió un montón de nuevos errores: valores de SOAPA en blanco, parámetros que no se leen. Estoy trabajando en eso ahora. –

+0

He tenido un puntaje menos perfecto en la interoperabilidad de WCF con otros entornos.SOAP11 parece ser menos interoperable que SOAP12, por lo que si su código Delphi le permite cambiar a SOAP12 fácilmente, le sugiero que explore esa opción. – Maurice

0

Gracias - esto ayudó mucho. Tuve problemas con un par de arrugas más. Para mí, el problema # 2 (SOAPAction) estaba contaminado porque OperationName no coincidía. El grupo .Net estandarizó la colocación de "In" al final de la SOAPAction, pero no de la Operación.
Así que yadda.yadda.com/whatever/services/%operationName% realmente necesita ser yadda.yadda.com/whatever/services/%operationName%In En este caso específico.

Me tomó bastante tiempo detectarlo, pero finalmente me di cuenta al probar en paralelo con SoapUI, que tenía diferentes SOAPActions que las que aparecían en la respuesta de error. Lo arreglé y funcionó. Pero esto fue después de haber luchado bastante tiempo tratando de descubrir qué debería haber en la configuración predeterminada de la cuenta en primer lugar. Nuevamente, SoapUI fue útil aquí.
De todos modos, si encuentra que obtiene este error: "La acción (lo que sea) no se puede procesar en el receptor, debido a una discrepancia de ContractFilter en el EndpointDispatcher ..." El primer paso es llenar la configuración predeterminada, y si los problemas persisten, compare el informe del error con lo que realmente debería estar allí.
HTH, Chris

+0

Hmm, obtengo: "El nombre del contenedor de elementos de entrada no coincide con el nombre de la operación" en la unidad importada por la herramienta wsdl. Tal vez ese es el problema, usted escribió ...? ¿Cómo corrigió ese error? En el lado C# o delphi? – John

Cuestiones relacionadas