2009-07-09 62 views
6

我写了一个相当简单的小C#web服务,通​​过WCF从独立的EXE托管。代码 - 有点简化 - 看起来像这样:如何从Delphi 2007中使用非IIS托管的WCF C#Web服务?

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(); 

其余的C#代码;上面的类VMPService实现了VMProvisionCore.IVMProvisionCore。

我可以很容易地创建一个使用此服务的Visual Studio 2008客户端应用程序。没问题。但是使用Delphi 2007是一个不同的问题。我可以使用Delphi中的WSDL导入器从(在这种情况下)检索WSDL http://bernard3:8000/VMWareProvisioning/Service?wsdl导入单元编译得很好。我必须手工初始化代理由于WSDL不包含一个URL(注意额外“/ CoreServices”如图所示的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; 

上面的代码,当它击中就会产生错误“CoreI.AuthenticateUser(Auth);”。错误是“无法处理该消息,因为该内容类型‘文本/ XML;字符集=‘UTF-8’不是预期的类型’应用/肥皂+ xml的;字符集= UTF-8

我怀疑我在某个地方出现了一个愚蠢的小错误,可能是在导入WSDL或连接选项时出现错误。谁能帮忙?

回答

4

找到了解决方案。这是多个部分,需要对C#端进行一些更改,更多的是德尔福方面。请注意,这是用Delphi 2007和Visual Studio 2008测试的。

C#端: 使用BasicHttpBinding而不是WSHttpBinding。

修正步骤1

​​

这种变化将解决上的Delphi侧的应用/肥皂+ xml的错误。

2007年德尔福方: 运行针对修改后的C#Web服务现在会产生这样的错误:

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

要解决此问题,添加SOAPActions到所有支持的接口。以下是我的代码示例;这必须毕竟由进口从 - WSDL-PAS-文件的初始化部分所作的InvRegistry的变化来完成:

修复步骤2

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

类型名称和URL应该从获得Delphi从WSDL生成导入文件和/或检查实际的WSDL。上面的例子适用于我自己的项目。这些代码更改之后,那么你的错误:

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

此错误是通过添加以下代码(学分http://www.bobswart.nl/weblog/Blog.aspx?RootId=5:798)解决。同样,这个新代码必须在WSDL到PAS文件初始化的所有InvRegistry内部。

修复步骤3

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

在这一点上,数据包将走个来回Delphi和C#之间 - 但参数将不能正常工作。 C#将接收所有参数作为空值,并且Delphi似乎没有正确接收响应参数。最后的代码步骤是使用稍微定制的THTTPRIO对象,该对象将允许使用文字参数。这部分的技巧是确保在获得接口后应用该选项;之前这样做是行不通的。这里是我的例子中的代码(只是片段)。

修复步骤4

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

而现在 - 我的2007年德尔福应用程序可以跟C#,独立的,非IIS,WCF Web服务!

0

我在delphi中使用C#web服务时也面临同样的问题。
Delphi 7.0/2005/2007不支持新的WSDL定义。
为此,您需要下载最新的WSDL导入程序(WSDLImp.exe)。它还将提供更新的delphi源代码传递文件的源代码。

+0

你知道我可以在哪里下载最新的WSDLImp.exe吗?我检查了Embarcadero网站,但找不到任何超出它的错误修复引用。 – 2009-07-21 14:46:42

1

这是由于SOAP版本不匹配造成的。 C#服务期待SOAP12消息并从Delphi应用程序接收SOAP11消息。根据你的情况,你需要改变双方中的任何一个。我不能真正评论德尔福方面。在WCF端,您可以使用默认为SOAP11的BasicHttpBinding,或者如果您需要更多控制,请使用指定SOAP11消息类型的CustomBinding。

+0

将C#服务器切换到BasicHttpBinding确实解决了application/soap + xml问题。但是,它打开了一大堆新的错误:空白的SOAPAction值,参数没有被读取。我现在正在处理这些问题。 – 2009-07-21 14:48:06

+0

我在WCF与其他环境进行互操作时得到了一个不太完美的分数。因此,如果您的Delphi代码允许您轻松切换到SOAP12,那么SOAP11似乎不易与SOAP12交互,所以我会建议您探索该选项。 – Maurice 2009-07-21 18:40:24

0

谢谢 - 这有很大的帮助。我遇到了更多的皱纹。对我来说,问题#2(SOAPAction)全部被堵塞,因为OperationName不匹配。 .Net小组标准化在SOAPAction结束时放置“In”,但不是操作。
因此yadda.yadda.com/whatever/services/%operationName% 确实需要是 yadda.yadda.com/whatever/services/%operationName%In 在这种情况下。

我花了很长时间才发现,但最终我发现通过与SoapUI并行测试,发现SOAPAction与错误响应中返回的SOAPAction不同。我解决了这个问题,并且工作。但是这是在经历了一段时间的努力之后,试图首先找出DefaultSOAPAction中的内容。再一次,SoapUI在这里很有帮助。
因此,无论如何,如果您发现此错误: “由于EndpointDispatcher中的ContractFilter不匹配,无法在接收方处理操作(无论)...” 第一步是填充DefaultSOAPAction,并且如果问题仍然存在,请将错误报告中的错误与真正应该存在的错误进行比较。
HTH,Chris