2009-07-09 2 views
6

Ho scritto un servizio web C# piuttosto semplice, ospitato da un file EXE autonomo tramite WCF. Il codice, in qualche modo semplificato, è il seguente:Come utilizzare Web service ospitato non IIS, WCF, C# da 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(); 

Il resto del codice C#; la classe VMPService sopra implementa VMProvisionCore.IVMProvisionCore.

posso facilmente creare un'applicazione client di Visual Studio 2008 che consuma questo servizio. Nessun problema. Ma usare Delphi 2007 è un problema diverso. Posso utilizzare l'importatore WSDL in Delphi per recuperare il WSDL da (in questo caso) http://bernard3:8000/VMWareProvisioning/Service?wsdl L'unità di importazione viene compilata correttamente. Devo inizializzare la delega a mano dal momento che il WSDL non contiene un URL (notare le extra "/ CoreServices" come mostrato nel codice 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; 

Il codice precedente genera un errore quando colpisce il "CoreI.AuthenticateUser (Auth);". L'errore è "non può elaborare il messaggio in quanto il tipo di contenuto 'text/xml; charset = "utf-8" non era il tipo previsto' application/soap + xml;. Charset = utf-8"

I sospetto di aver avuto uno stupido piccolo errore da qualche parte, forse durante l'importazione del WSDL o nelle opzioni di connessione o qualcosa del genere. Qualcuno può aiutare?

risposta

4

Trovato la soluzione. Sono parti multiple e richiedono alcune modifiche al lato C#, più al lato Delphi. Si noti che questo è stato testato con Delphi 2007 e Visual Studio 2008.

Lato C#: Utilizzare BasicHttpBinding anziché WSHttpBinding.

Fix Fase 1

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

Questa modifica risolvere gli errori di applicazione/soap + xml sul lato Delphi.

Delphi 2007 lato: esecuzione contro il servizio Web C# modificato sarà ora generare errori come questo:

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).'

Per risolvere questo problema, aggiungere SOAPActions a tutte le vostre interfacce supportate.Ecco l'esempio dal mio codice; questo deve essere fatto dopo che tutte le modifiche InvRegistry apportate dalla sezione di inizializzazione del import-da-WSDL-PAS-file:

Fix Fase 2

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

Il nome del tipo e l'URL dovrebbe essere ottenibile dalla il file di importazione generato da Delphi dal WSDL e/o un'ispezione del WSDL effettivo. L'esempio precedente era per il mio progetto. Dopo queste modifiche al codice? Allora ti l'errore:

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....

Questo errore viene risolto aggiungendo il seguente codice (crediti alle http://www.bobswart.nl/weblog/Blog.aspx?RootId=5:798). Di nuovo, questo nuovo codice deve essere dopo tutto il materiale InvRegistry nell'inizializzazione del file WSDL-PAS.

Fix Fase 3

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

A questo punto, i pacchetti andranno avanti e indietro tra Delphi e C# - ma i parametri non funzioneranno correttamente. Il C# riceverà tutti i parametri come null e Delphi non sembra ricevere correttamente i parametri di risposta. Il passo finale del codice consiste nell'utilizzare un oggetto THTTPRIO leggermente personalizzato che consentirà parametri letterali. Il trucco per questa parte è assicurarsi che l'opzione sia applicata DOPO che l'interfaccia è stata ottenuta; farlo prima non funzionerà. Ecco il codice dal mio esempio (solo frammenti).

Fix Fase 4

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

E ora - il mio Delphi 2007 applicazione può parlare con il C#, stand-alone, non-IIS, servizio Web WCF!

0

Ho anche affrontato lo stesso problema quando si consumava il servizio web C# in delphi.
Delphi 7.0/2005/2007 non supporta nuove definizioni WSDL.
Per questo, è necessario scaricare l'ultimo WSDL Importer (WSDLImp.exe). Fornirà inoltre il codice sorgente per i file di passaggio del codice sorgente aggiornati delphi.

+0

Sai dove posso scaricare i latest WSDLImp.exe? Ho controllato il sito Web di Embarcadero, ma non sono riuscito a trovare nulla al di là dei riferimenti alla correzione degli errori. –

1

Ciò è causato da una mancata corrispondenza della versione SOAP. Il servizio C# si aspetta un messaggio SOAP12 e riceve un messaggio SOAP11 dall'app Delphi. A seconda della situazione, è necessario modificare uno dei due lati. Non posso davvero commentare il lato Delphi. Sul lato WCF è possibile utilizzare BasicHttpBinding, che per impostazione predefinita SOAP11 o, se è necessario un maggiore controllo, utilizzare un CustomBinding che specifica un tipo di messaggio SOAP11.

+0

Il passaggio del server C# a BasicHttpBinding ha risolto il problema application/soap + xml. Tuttavia, ha aperto un sacco di nuovi errori: valori di SOAPAction vuoti, parametri non letti. Sto lavorando a quelli adesso. –

+0

Ho avuto un punteggio inferiore a quello perfetto nell'interpolazione WCF con altri ambienti.SOAP11 sembra essere meno intercettabile di SOAP12 quindi se il tuo codice Delphi ti consente di passare a SOAP12 facilmente ti suggerirei di esplorare tale opzione. – Maurice

0

Grazie - questo ha aiutato molto. Ho avuto problemi con un altro paio di rughe. Per me, il problema n. 2 (SOAPAction) è stato messo in dubbio perché l'OperationName non corrispondeva. Il gruppo .Net standardizzato sul piazzamento di "In" alla fine di SOAPAction, ma non sull'operazione.
Quindi yadda.yadda.com/whatever/services/%operationName% ha davvero bisogno di essere yadda.yadda.com/whatever/services/%operationName%In In questo caso specifico.

Mi ci è voluto un po 'per individuarlo, ma alla fine ho notato testando in parallelo con SoapUI, che aveva SOAPActions diverse da quelle che tornavano nella risposta all'errore. L'ho risolto e ha funzionato. Ma questo è stato dopo aver lottato per un po 'cercando di capire cosa dovrebbe essere in DefaultSOAPAction in primo luogo. Ancora una volta, SoapUI è stato utile qui.
Ad ogni modo, se si riscontra questo errore: "L'azione (qualunque) non può essere elaborata sul ricevitore, a causa di una mancata corrispondenza di ContractFilter su EndpointDispatcher ..." Il primo passaggio consiste nel popolare DefaultSOAPAction, e se i problemi persistono, confronta quello segnalato nell'errore con quello che dovrebbe essere realmente lì.
HTH, Chris

+0

Hmm, ottengo: "Il nome del wrapper dell'elemento di input non corrisponde al nome dell'operazione" nell'unità importata dallo strumento wsdl. Forse è questo il problema, hai scritto ..? come hai risolto quell'errore? Sul lato C# o delphi? – John