Как потреблять не-IIS хостинг, WCF, веб-сервис C # из Delphi 2007?

Я написал довольно простой небольшой веб-сервис C #, размещенный из автономного EXE через WCF. Код – несколько упрощенный – выглядит так:

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 #; class 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 – это другая проблема. Я могу использовать импортер WSDL в Delphi для извлечения 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);». Ошибка: « Не удается обработать сообщение, потому что тип содержимого« 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; 

Это изменение разрешит ошибки application / soap + xml на стороне Delphi.

Сторона Delphi 2007: запуск против модифицированного веб-сервиса C # теперь приведет к следующим ошибкам:

Класс исключений ERemotableException с сообщением «Сообщение с действием» не может быть обработан в приемнике из-за несоответствия ContractFilter в EndpointDispatcher. Это может быть из-за несоответствия контракта (несоответствие действий между отправителем и получателем) или несоответствия привязки / безопасности между отправителем и получателем. Убедитесь, что отправитель и получатель имеют один и тот же контракт и одну и ту же привязку (включая требования безопасности, например сообщение, транспорт, нет).

Чтобы устранить эту проблему, добавьте SOAPActions ко всем поддерживаемым интерфейсам. Вот пример из моего кода; это должно быть сделано ПОСЛЕ всех изменений InvRegistry, сделанных в разделе инициализации импорта-из-WSDL-PAS-файла:

Исправить шаг 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 ). Опять же, этот новый код должен быть после всего содержимого InvRegistry в инициализации файла WSDL-to-PAS.

Исправить шаг 3

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

В этот момент пакеты будут перемещаться между Delphi и C #, но параметры не будут работать должным образом. C # получит все параметры как null, и Delphi, похоже, не получает параметры ответа должным образом. Конечным этапом кода является использование слегка настроенного объекта THTTPRIO, который позволит использовать литеральные параметры. Трюк этой части заключается в том, чтобы убедиться, что опция применяется ПОСЛЕ получения интерфейса; делать это раньше не получится. Вот код из моего примера (просто fragmentы).

Исправить шаг 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!

Это вызвано несоответствием версии SOAP. Служба C # ожидает сообщения SOAP12 и получает сообщение SOAP11 из вашего приложения Delphi. В зависимости от вашей ситуации вам нужно изменить любую из двух сторон. Я не могу прокомментировать сторону Delphi. На стороне WCF вы можете использовать BasicHttpBinding, который по умолчанию соответствует SOAP11, или, если вам нужно больше контроля, используйте CustomBinding, определяющий тип сообщения SOAP11.

Я также столкнулся с такой же проблемой при использовании веб-службы C # в delphi.
Delphi 7.0 / 2005/2007 не поддерживает новые определения WSDL.
Для этого вам нужно загрузить последнюю версию WSDL Importer (WSDLImp.exe). Он также предоставит исходный код для обновленных файлов доступа к исходному коду delphi.

Спасибо – это очень помогло. У меня были проблемы с еще несколькими морщинами. Для меня проблема № 2 (SOAPAction) была полностью испорчена, потому что OperationName не совпало. Группа .Net стандартизировала размещение «In» в конце SOAPAction, но не Operation.
Поэтому yadda.yadda.com/whatever/services/%operationName% действительно нужно быть yadda.yadda.com/whatever/services/%operationName%In В этом конкретном случае.

Мне потребовалось некоторое время, чтобы заметить это, но я, наконец, заметил, что параллельно тестировал SoapUI, что у него разные SOAPActions, чем те, которые возвращаются в ответ об ошибке. Я исправил это, и это сработало. Но это продолжалось довольно долго, пытаясь выяснить, что должно быть в DefaultSOAPAction в первую очередь. Опять же, SoapUI был полезен здесь.
Так или иначе, если вы обнаружите, что получите эту ошибку: «Действие (независимо) не может быть обработано в приемнике из-за несоответствия ContractFilter в EndpointDispatcher …» Первым шагом является заполнение DefaultSOAPAction, и если проблемы сохраняются, сравните тот, который сообщается в ошибке с тем, что действительно должно быть там.
HTH, Chris