Как использовать веб-сервис WCF, C #, не размещенный на IIS, из Delphi 2007? - PullRequest
6 голосов
/ 09 июля 2009

Я написал довольно простой маленький веб-сервис на 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 ]

4 голосов
/ 21 июля 2009

Нашел решение. Он состоит из нескольких частей и требует нескольких изменений на стороне 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, внесенных разделом инициализации import-from-WSDL-PAS-файла:

Fix Step 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, который учитывает литеральные параметры. Хитрость в этой части заключается в том, чтобы убедиться, что опция применяется ПОСЛЕ интерфейса был получен; делать это раньше не получится. Вот код из моего примера (только фрагменты).

Фикс Шаг 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!

1 голос
/ 21 июля 2009

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

0 голосов
/ 15 октября 2009

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

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

0 голосов
/ 21 июля 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...