Вызов службы 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 или выше
- Запустите командную строку разработчика VS 2017.
-
Перейдите в файл 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>
-
Восстановить решение.
- Измените каталог на местоположение вашего проекта из командной строки VS.
- команда запуска: svcutil SOAP_URL? wsdl; пример: example.com/test/testing?wsdl Это создаст справочные файлы и файл output.config в вашем проекте.
- В .Net Core нет файлов app.config или web.config, но файл output.config будет служить для привязки SOAP.
Вариант 2 В случае, если вам нужно сослаться на несколько сервисов SOAP,
- Создайте новый проект библиотеки классов, используйте .Net Framework 4.5.1.NET Framework имеет значение, так как я увидел, что эталонные файлы, сгенерированные из контракта, неверны, если .Net Framework является последней.
- Добавьте сервисную ссылку, щелкнув правой кнопкой мыши по ссылке.
- Обратитесь к проекту библиотеки классов из вашего основного проекта .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
.