如何从Delphi 2007消费非IIS托管,WCF,C#Web服务? [英] How to consume non-IIS hosted, WCF, C# web service from Delphi 2007?

查看:142
本文介绍了如何从Delphi 2007消费非IIS托管,WCF,C#Web服务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经写了一个相当简单的小C#web服务,通​​过WCF从独立的EXE托管。代码有些简化 - 如下所示:

 命名空间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);

尝试
{
selfHost.AddServiceEndpoint(typeof(IVMProvisionCore),myBinding,CoreServices);

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

//添加MEX端点
selfHost.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,MetadataExchangeBindings.CreateMexHttpBinding(),mex);

selfHost.Open();
Console.WriteLine(服务已准备就绪);
Console.ReadLine();

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

 命名空间VMProvisionCore 
{
[ServiceContract(Namespace = http://Cisco.VMProvision.Core,ProtectionLevel = System.Net.Security.ProtectionLevel.None)]
public interface IVMProvisionCore
{
[OperationContract]
bool AuthenticateUser字符串用户名,字符串密码);
}

我可以轻松地创建一个使用此服务的Visual Studio 2008客户端应用程序。没问题。但是使用Delphi 2007是一个不同的问题。我可以使用Delphi中的WSDL导入器从(在这种情况下) http:// bernard3:8000 / VMWareProvisioning / Service?wsdl 导入单元编译很好。我必须手动初始化代理,因为WSDL不包含URL(请注意C#代码中显示的额外的/ CoreServices):

  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);
结束

上述代码在CoreI.AuthenticateUser(Auth)中出现错误。错误是无法处理邮件,因为内容类型'text / xml; charset =utf-8不是预期的类型'application / soap + xml; charset = utf-8。



我怀疑我在某个地方有一个愚蠢的小错误,也许在导入WSDL或连接选项或某些东西时。任何人都可以帮忙?

解决方案

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



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



修复步骤1

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

此更改将解决Delphi一方的应用程序/ soap + xml错误。



Delphi 2007端:
针对修改后的C#web服务运行现在将生成如下错误:


异常类ERemotableException
与消息'Action
''的消息无法在
接收器处理,因为EndpointDispatcher上的ContractFilter
不匹配。
这可能是因为
合同不匹配(发件人和接收方之间的操作
不匹配)或
发送者和接收方之间的
绑定/安全性不匹配。检查
发件人和接收方是否具有相同的
合同和相同的绑定
(包括安全性要求,例如
消息,传输,无)。


要解决此问题,请将SOAPActions添加到所有支持的界面。这是我代码中的例子必须在导入从WSDL-PAS文件的初始化部分进行的所有InvRegistry更改之后完成此操作:



修复步骤2

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

类型名称和URL应该可以从Delphi生成的从WSDL导入的文件和/或检查实际的WSDL。上面的例子是我自己的项目。在这些代码更改之后,您将出现错误:


异常类ERemotableException
与消息'格式化程序投掷
异常尝试反序列化
消息:反序列化
主体的请求消息
操作错误

通过添加以下代码来解决此错误:( http://www.bobswart.nl/weblog/Blog.aspx?RootId=5:798 )。再次,这个新的代码必须是在初始化WSDL到PAS文件之后所有的InvRegistry的东西。



修复步骤3 / p>

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

在这一点上,数据包将在Delphi和C#之间来回移动,但参数无法正常工作正确的C#将接收所有参数作为null,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];

现在 - 我的Delphi 2007应用程序可以与C#,独立,非IIS, WCF网页服务!


I've written a fairly simple little C# web service, hosted from a standalone EXE via WCF. The code - somewhat simplified - looks like this:

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

The rest of the C# code; the class VMPService above implements VMProvisionCore.IVMProvisionCore.

namespace VMProvisionCore
{
[ServiceContract(Namespace = "http://Cisco.VMProvision.Core", ProtectionLevel = System.Net.Security.ProtectionLevel.None)]
public interface IVMProvisionCore
{
    [OperationContract]
    bool AuthenticateUser(string username, string password);
}

I can easily create a Visual Studio 2008 client application that consumes this service. No problems. But using Delphi 2007 is a different issue. I can use the WSDL importer in Delphi to retrieve the WSDL from (in this case) http://bernard3:8000/VMWareProvisioning/Service?wsdl The import unit compiles just fine. I have to initialize the proxy by hand since the WSDL doesn't contain a URL (notice the extra "/CoreServices" as shown in the C# code):

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;

The above code will generate an error when it hits the "CoreI.AuthenticateUser(Auth);". The error is "Cannot process the message because the content type 'text/xml; charset="utf-8" was not the expected type 'application/soap+xml; charset=utf-8."

I suspect that I've got a stupid little error somewhere, perhaps during the import of the WSDL or in the connection options or something. Can anyone help?

解决方案

Found the solution. It's multiple parts and requires a few changes to the C# side, more to the Delphi side. Note that this was tested with Delphi 2007 and Visual Studio 2008.

C# side: Use BasicHttpBinding rather than WSHttpBinding.

Fix Step 1

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

This change will resolve the application/soap+xml errors on the Delphi side.

Delphi 2007 side: Running against the modified C# web service will now generate errors like this:

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

To resolve this problem, add SOAPActions to all your supported interfaces. Here's the example from my code; this must be done AFTER all of the InvRegistry changes made by the import-from-WSDL-PAS-file's initialization section:

Fix Step 2

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

The type name and URL should be obtainable from the Delphi generated import file from the WSDL and/or an inspection of the actual WSDL. The above example was for my own project. After these code changes, then you'll the 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....

This error is resolved by adding the following code (credits to http://www.bobswart.nl/weblog/Blog.aspx?RootId=5:798). Again, this new code must be after all the InvRegistry stuff in the initialization of the WSDL-to-PAS file.

Fix Step 3

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

At this point, packets will go back and forth between Delphi and C# - but parameters won't work properly. The C# will receive all parameters as nulls and Delphi doesn't seem to be receiving response parameters properly. The final code step is to use a slightly customized THTTPRIO object that will allow for literal parameters. The trick to this part is to make sure that the option is applied AFTER the interface has been obtained; doing it before won't work. Here's the code from my example (just snippets).

Fix Step 4

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

And now - my Delphi 2007 app can talk to the C#, stand-alone, non-IIS, WCF web service!

这篇关于如何从Delphi 2007消费非IIS托管,WCF,C#Web服务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆