Вызов службы SOAP в .net Core

Я портирую код.net 4.6.2 в проект .net Core, который вызывает службу SOAP. В новом коде я использую С# (из-за некоторых причин конфигурации я просто не могу вспомнить, почему именно сейчас).

Но я получаю следующее исключение.

Произошла ошибка при получении ответа HTTP на https://someurl.com/ws/Thing.pub.ws:Something. Это может быть связано с привязкой конечной точки службы, не использующей протокол HTTP. Это также может быть связано с тем, что сервер HTTP-запроса прерывается, и, возможно, из-за отключения службы. Подробнее см. Журналы сервера.

Код, который его бросает

try
{
    var binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport);
    binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

    var endpoint = new EndpointAddress(new Uri("https://someurl.com/ws/TheEndpoint.pub.ws:AService"));

    var thing= new TheEndpoint.AService_PortTypeClient(binding, endpoint);
    thing.ClientCredentials.UserName.UserName = "usrn";
    thing.ClientCredentials.UserName.Password = "passw";

    var response = await thing.getSomethingAsync("id").ConfigureAwait(false);

}
finally
{
    await thing.CloseAsync().ConfigureAwait(false);
}

Основываясь на старой конфигурации, где он работает, вызывая услугу, это то, чего я не вижу?

<bindings>
  <basicHttpsBinding>
    <binding name="TheEndpoint_pub_ws_AService_Binder" closeTimeout="00:02:00"
        openTimeout="00:02:00" receiveTimeout="00:03:00" sendTimeout="00:03:00">
      <security mode="Transport">
        <transport clientCredentialType="Basic" />
        <message clientCredentialType="UserName" algorithmSuite="Default" />
      </security>
    </binding>
  </basicHttpsBinding>
</bindings>
<client>
  <endpoint address="https://someurl.com/ws/Thing.pub.ws:AService"
      binding="basicHttpsBinding" bindingConfiguration="TheEndpoint_pub_ws_AService_Binder"
      contract="TheEndpoint.AService_PortType" name="TheEndpoint_pub_ws_AService_Port" />
</client>

Я просто не могу найти много информации об этом в Интернете. Надеюсь, ты поможешь мне.

UPDATE По предложению Sixto Saez я получил конечную точку, чтобы показать ее ошибку, и это

HTTP-запрос несанкционирован с помощью схемы аутентификации клиента "Basic". Аутентификационный заголовок, полученный с сервера, был "Basic realm =" Сервер интеграции ", кодировка =" UTF-8 "".

Я попытаюсь выяснить, что делать и опубликовать результат здесь, если он будет успешным.

ОБНОВЛЕНИЕ 2

Теперь я попытался перейти к новому синтаксису с помощью этого кода здесь

ChannelFactory<IAService> factory = null;
IAService serviceProxy = null;
Binding binding = null;

try
{
   binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport);

   factory = new ChannelFactory<IAService>(binding, new EndpointAddress(new Uri("https://someurl.com/ws/TheEndpoint.pub.ws:AService")));            
   factory.Credentials.UserName.UserName = "usrn";
   factory.Credentials.UserName.Password = "passw";

   serviceProxy = factory.CreateChannel();

   var result = await serviceProxy.getSomethingAsync("id").ConfigureAwait(false);

    factory.Close();
    ((ICommunicationObject)serviceProxy).Close();  
}
catch (MessageSecurityException ex)
{
    //error caught here
    throw;
}

но я все равно получаю ту же (немного другую) ошибку. Теперь он имеет "Анонимный" вместо "Базовый" и теперь отсутствует ", кодировка =" UTF-8 "в конце.

HTTP-запрос неавторизован с помощью схемы проверки подлинности клиента "Аноним". Аутентификационный заголовок, полученный с сервера, был "Basic realm =" Integration Server ".

Является ли проблема на моей стороне или на серверах?

Очевидно, что у моих SOAP-навыков сейчас очень мало времени, но я просто пробовал все конфигурационные комбо, с которыми я могу думать с этим новым подходом без везения. Надеюсь, кто-то может указать мне в правильном направлении.

Ответ 1

Хорошо, этот ответ для тех, кто пытается подключиться к службе WCF из проекта .net Core.

Вот решение моей проблемы с использованием нового синтаксиса/библиотеки .net Core WCF.

BasicHttpBinding basicHttpBinding = null;
EndpointAddress endpointAddress = null;
ChannelFactory<IAService> factory = null;
IAService serviceProxy = null;

try
{
    basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
    basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
    endpointAddress = new EndpointAddress(new Uri("https://someurl.com/ws/TheEndpoint.pub.ws:AService"));
    factory = new ChannelFactory<IAService>(basicHttpBinding, endpointAddress);

    factory.Credentials.UserName.UserName = "usrn";
    factory.Credentials.UserName.Password = "passw";
    serviceProxy = factory.CreateChannel();

    using (var scope = new OperationContextScope((IContextChannel)serviceProxy))
    {
        var result = await serviceProxy.getSomethingAsync("id").ConfigureAwait(false);
    }

    factory.Close();
    ((ICommunicationObject)serviceProxy).Close();
}
catch (MessageSecurityException ex)
{
     throw;
}
catch (Exception ex)
{
    throw;
}
finally
{
    // *** ENSURE CLEANUP (this code is at the WCF GitHub page *** \\
    CloseCommunicationObjects((ICommunicationObject)serviceProxy, factory);
}

ОБНОВИТЬ

Я получил следующее исключение, используя код выше

Этот OperationContextScope располагается не по порядку.

Который, кажется, что- то, что сломано (или нуждается в решении) командой WCF.

Поэтому я должен был сделать следующее, чтобы заставить его работать (на основе этой проблемы GitHub)

basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

factory = new ChannelFactory<IAService_PortType>(basicHttpBinding, new EndpointAddress(new Uri("https://someurl.com/ws/TheEndpoint.pub.ws:AService")));
factory.Credentials.UserName.UserName = "usern";
factory.Credentials.UserName.Password = "passw";
serviceProxy = factory.CreateChannel();
((ICommunicationObject)serviceProxy).Open();
var opContext = new OperationContext((IClientChannel)serviceProxy);
var prevOpContext = OperationContext.Current; // Optional if there no way this might already be set
OperationContext.Current = opContext;

try
{
    var result = await serviceProxy.getSomethingAsync("id").ConfigureAwait(false);

    // cleanup
    factory.Close();
    ((ICommunicationObject)serviceProxy).Close();
}
finally
{
  // *** ENSURE CLEANUP *** \\
  CloseCommunicationObjects((ICommunicationObject)serviceProxy, factory);
  OperationContext.Current = prevOpContext; // Or set to null if you didn't capture the previous context
}

Но ваши требования, вероятно, будут другими. Итак, вот ресурсы, которые могут вам понадобиться, чтобы помочь вам подключиться к службе WCF:

Тесты мне очень помогли, но их было довольно сложно найти (я помог, спасибо, Женлан, что ответил на мою проблему с wcf github)

Ответ 2

Для использования службы SOAP из ядра .NET добавление подключенной службы из пользовательского интерфейса проекта не работает.

Вариант 1: использовать dotnet-svcutil CLI. Необходимые условия: VS 2017, версия 15.5 или выше

  1. Запустите командную строку разработчика VS 2017.
  2. Перейдите в файл app.csproj и добавьте ссылки ниже:

    <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.9" />
    <PackageReference Include="System.ServiceModel.Http" Version="4.5.3" />
    </ItemGroup>
    <ItemGroup>
    <DotNetCliToolReference Include="dotnet-svcutil" Version="1.0.*" />
    </ItemGroup>
    
  3. Восстановить решение.

  4. Измените каталог на местоположение вашего проекта из командной строки VS.
  5. команда запуска: svcutil SOAP_URL? wsdl; пример: example.com/test/testing?wsdl Это создаст справочные файлы и файл output.config в вашем проекте.
  6. В .Net Core нет файлов app.config или web.config, но файл output.config будет служить для привязки SOAP.

Вариант 2 В случае, если вам нужно сослаться на несколько сервисов SOAP,

  1. Создайте новый проект библиотеки классов, используйте .Net Framework 4.5.1.NET Framework имеет значение, так как я увидел, что эталонные файлы, сгенерированные из контракта, неверны, если .Net Framework является последней.
  2. Добавьте сервисную ссылку, щелкнув правой кнопкой мыши по ссылке.
  3. Обратитесь к проекту библиотеки классов из вашего основного проекта .Net.

Ответ 3

Для тех, кто пытается сделать то же самое с NTLM и .Net Core и интересуется, как определяются некоторые переменные, я пояснил код так:

IAService_PortType - это сервисная ссылка, которую вы создали, если следовали руководству на https://joshuachini.com/2017/07/13/calling-a-soap-service-from-asp-net-core-or-net-core/

BasicHttpBinding basicHttpBinding = 
    new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
// Setting it to Transport will give an exception if the url is not https
basicHttpBinding.Security.Transport.ClientCredentialType = 
    HttpClientCredentialType.Ntlm;

ChannelFactory<IAService_PortType> factory = 
    new ChannelFactory<IAService_PortType>(basicHttpBinding, 
    new EndpointAddress(
        new Uri("https://someurl.com/ws/TheEndpoint.pub.ws:AService")));
factory.Credentials.Windows.ClientCredential.Domain = domain;
factory.Credentials.Windows.ClientCredential.UserName = user;
factory.Credentials.Windows.ClientCredential.Password = pass;
IAService_PortType serviceProxy = factory.CreateChannel();
((ICommunicationObject)serviceProxy).Open();

try
{
    var result = serviceProxy.getSomethingAsync("id").Result;

}
finally
{
    // cleanup
    factory.Close();
    ((ICommunicationObject)serviceProxy).Close();
}

Ответ 4

Я использовал приведенные выше строки кода для внедрения URL SOAP в мой основной проект .net. Получение ниже сообщения об ошибке в двух разных сценариях:

Сценарий 1: когда не предоставлены значения для входных параметров/переменной запроса

Сообщение об ошибке:

'oracle.apps.fnd.soa.util.SOAException: ServiceProcessingError: System

ErrorServiceGenerationError: Ошибка при создании ответа

MessageServiceGenerationError: Ошибка при получении переведенной ошибки messagenull '

Сценарий 2. Когда предоставляется входной параметр и переменная запроса

Сообщение об ошибке:

Пользователь не авторизован для выполнения услуги. '

Ответ 5

Поэтому я должен был сделать это и использовать Инструмент для предоставления ссылок на веб-службы WCF.

В соответствии с ответами, подобными приведенным здесь, очевидная потребность во всем круговом бизнесе с Bindings, Factories и Proxies казалась странной, учитывая, что все это было частью импортированного класса.

Не имея возможности найти простое официальное "HowTo", я опубликую свои выводы о простейшей настройке, которую мне удалось собрать, чтобы соответствовать моим требованиям с помощью дайджест-аутентификации:

    ServiceName_PortClient client = new ServiceName_PortClient();
    //GetBindingForEndpoint returns a BasicHttpBinding
    var httpBinding = client.Endpoint.Binding as BasicHttpBinding;
    httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Digest;
    client.ClientCredentials.HttpDigest.ClientCredential = new NetworkCredential("Username", "Password", "Digest");
    var result = await client.GetResultAsync();

Теперь, если вам не нужно выполнять какую-либо аутентификацию, просто выполните:

    ServiceName_PortClient client = new ServiceName_PortClient();
    var result = await client.GetResultAsync();

Должно быть достаточно.

Класс ServiceName_PortClient был сгенерирован как таковой с помощью инструмента импорта, где ServiceName было названием службы, которую я импортировал.

Конечно, кажется, что в импортированном коде больше подходит размещение конфигурации в частичном классе ServiceName_PortClient в соответствии с:

    public partial class ServiceName_PortClient
    {
        static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials)
        {
            var httpBinding = serviceEndpoint.Binding as BasicHttpBinding;
            httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Digest;
            clientCredentials.HttpDigest.ClientCredential = new NetworkCredential("Username", "Password", "Realm");
        }
    }

Ответ 6

Вы можете использовать класс ChannelFactory даже в чистом виде dotnet core. Это довольно просто.

var binding = new BasicHttpsBinding();
var endpoint = new EndpointAddress(new Uri("https://<myhost>/SimpleSOAPService.svc"));
dynamic channelFactory = new ChannelFactory<ISimpleSOAPService>(binding, endpoint);
var serviceClient = channelFactory.CreateChannel();
var result = serviceClient.Ping();
channelFactory.Close();

Вы можете найти работающий пример GitHub здесь.

Не забывайте переключаться между BasicHttpBinding и BasicHttpsBinding в зависимости от того, используете ли вы HTTP или HTTPS в URL.