[英]How to consume non-IIS hosted, WCF, C# web service from Delphi 2007?
我編寫了一個相當簡單的小型C#Web服務,通過WCF從獨立的EXE托管。 代碼 - 有點簡化 - 看起來像這樣:
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。
namespace VMProvisionCore
{
[ServiceContract(Namespace = "http://Cisco.VMProvision.Core", ProtectionLevel = System.Net.Security.ProtectionLevel.None)]
public interface IVMProvisionCore
{
[OperationContract]
bool AuthenticateUser(string username, string password);
}
我可以輕松地創建使用此服務的Visual Studio 2008客戶端應用程序。 沒問題。 但是使用Delphi 2007是一個不同的問題。 我可以使用Delphi中的WSDL導入器從(在這種情況下) http:// bernard3:8000 / VMWareProvisioning / Service?wsdl中檢索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);
end;
上面的代碼在遇到“CoreI.AuthenticateUser(Auth);”時會產生錯誤。 錯誤是“ 無法處理消息,因為內容類型'text / xml; charset =”utf-8“不是預期類型'application / soap + xml; charset = utf-8。 ”
我懷疑我在某處有一個愚蠢的小錯誤,可能是在導入WSDL或連接選項之類的時候。 有人可以幫忙嗎?
找到了解決方案。 它是多個部分,需要對C#端進行一些更改,對Delphi端更多。 請注意,這是使用Delphi 2007和Visual Studio 2008測試的。
C#side:使用BasicHttpBinding而不是WSHttpBinding。
修復第1步
BasicHttpBinding myBinding = new BasicHttpBinding();
myBinding.Security.Mode = BasicHttpSecurityMode.None;
此更改將解決Delphi端的application / soap + xml錯誤。
Delphi 2007方面:針對修改后的C#Web服務運行現在將生成如下錯誤:
由於EndpointDispatcher上的ContractFilter不匹配,因此無法在接收方處理帶有消息'帶有Action的消息'的異常類ERemotableException。 這可能是由於合同不匹配(發送方與接收方之間的操作不匹配)或發送方與接收方之間的綁定/安全性不匹配。 檢查發送方和接收方是否具有相同的合同和相同的綁定(包括安全要求,例如消息,傳輸,無)。
要解決此問題,請將SOAPActions添加到所有支持的接口。 這是我的代碼中的示例; 這必須在import-from-WSDL-PAS-file的初始化部分所做的所有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-to-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];
現在 - 我的Delphi 2007應用程序可以與C#,獨立,非IIS,WCF Web服務進行通信!
這是由SOAP版本不匹配引起的。 C#服務期待SOAP12消息並從您的Delphi應用程序接收SOAP11消息。 根據您的情況,您需要更改雙方中的任何一方。 我無法真正評論德爾福方面。 在WCF端,您可以使用默認為SOAP11的BasicHttpBinding,或者,如果需要更多控制,請使用指定SOAP11的消息類型的CustomBinding。
在delphi中使用C#Web服務時,我也遇到了同樣的問題。
Delphi 7.0 / 2005/2007不支持新的WSDL定義。
為此,您需要下載最新的WSDL Importer(WSDLImp.exe)。 它還將提供更新的delphi源代碼傳遞文件的源代碼。
謝謝 - 這幫了很多忙。 我有幾個皺紋的麻煩。 對我來說,問題#2(SOAPAction)都被搞砸了,因為OperationName不匹配。 .Net小組標准化了在SOAPAction結尾處放置“In”,但沒有標准化操作。
所以yadda.yadda.com/whatever/services/%operationName%確實需要是yadda.yadda.com/whatever/services/%operationName%In在這種特定情況下。
我花了很長時間才發現這一點,但我最終注意到與SoapUI並行測試,它與錯誤響應中返回的SOAPActions不同。 我解決了這個問題,並且有效。 但這是在經過一段時間努力試圖弄清楚DefaultSOAPAction應該是什么之后。 再次,SoapUI在這里很有幫助。
所以無論如何,如果你發現你得到這個錯誤:“由於EndpointDispatcher上的ContractFilter不匹配,無法在接收者處理動作(無論如何)......”第一步是填充DefaultSOAPAction,如果問題仍然存在,比較錯誤中報告的那個與真正應該存在的那個。
HTH,克里斯
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.