Как использовать веб-службу без 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, и если проблемы сохраняются, сравните сообщение об ошибке с тем, что действительно должно быть там.
ХТ, Крис