Как использовать веб-службу без IIS hosted, 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#; класс 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 или в параметрах подключения или что-то еще. Кто-нибудь может помочь?

4 ответов


нашел решение. Это несколько частей и требует нескольких изменений на стороне c#, больше на стороне Delphi. Обратите внимание, что это было протестировано с Delphi 2007 и Visual Studio 2008.

сторона C# : Используйте BasicHttpBinding, а не WSHttpBinding.

Исправить Шаг 1

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

это изменение устранит ошибки приложения / soap+xml на стороне Delphi.

Delphi 2007 сторона: Запуск с измененной веб-службой C# теперь будет генерировать такие ошибки:

исключение класса ERemotableException с сообщением ' сообщение с действием "не может быть обработан в приемник, из-за ContractFilter несоответствие в EndpointDispatcher. Это может быть из-за несоответствие контракта (несоответствующие действия между отправителем и получателем) или вязка/несоответствие между отправитель и получатель. Проверь это. отправитель и получатель имеют то же самое договор и обязательное (включая требования безопасности, например Сообщение, Транспорт, Нет).'

чтобы устранить эту проблему, добавьте SOAPActions во все поддерживаемые интерфейсы. Вот пример из моего кода; это должно быть сделано после всех изменений InvRegistry, сделанных разделом инициализации import-from-WSDL-PAS-file:

Исправить Шаг 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# будет получать все параметры как нули, а 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!


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


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


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

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