[英]Calling a SOAP service in .net Core
I´m porting a .net 4.6.2 code to a .net Core project , that calls a SOAP service.我正在将 .net 4.6.2 代码移植到调用 SOAP 服务的.net Core 项目。 In the new code I´m using C# (because of some config reasons I just can´t remember why right now).
在新代码中,我使用 C#(由于某些配置原因,我现在不记得为什么了)。
But I´m getting the following exception.但我收到以下异常。
An error occurred while receiving the HTTP response to https://someurl.com/ws/Thing.pub.ws:Something .
接收对https://someurl.com/ws/Thing.pub.ws:Something的 HTTP 响应时出错。 This could be due to the service endpoint binding not using the HTTP protocol.
这可能是由于服务端点绑定未使用 HTTP 协议。 This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down).
这也可能是由于服务器中止了 HTTP 请求上下文(可能是由于服务关闭)。 See server logs for more details.
有关更多详细信息,请参阅服务器日志。
The code that is throwing it is抛出它的代码是
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);
}
Based on the old config where it works calling the service is like this, what am I missing ?基于它调用服务的旧配置是这样的,我错过了什么?
<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>
I´m just unable to find lot of information on this online.我只是无法在网上找到很多关于此的信息。 Hope you can help me.
希望你能帮助我。
UPDATE Per Sixto Saez suggestion I got the endpoint to reveal its error and it is根据 Sixto Saez 的建议更新我得到了端点来揭示它的错误,它是
The HTTP request is unauthorized with client authentication scheme 'Basic'.
HTTP 请求未经授权,客户端身份验证方案为“基本”。 The authentication header received from the server was 'Basic realm="Integration Server", encoding="UTF-8"'.
从服务器收到的身份验证标头是“基本领域=“集成服务器”,编码=“UTF-8””。
I´ll try to find out what to do and post the result here if successful.如果成功,我会尝试找出该怎么做并在此处发布结果。
UPDATE 2更新 2
Ok now I tried to move to the new syntax with this code here好的,现在我尝试使用此代码移至新语法here
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;
}
but I still get the same (slightly different) error.但我仍然得到相同(略有不同)的错误。 It now has 'Anonymous' instead of 'Basic' and is now missing ", encoding="UTF-8" at the end.
它现在有 'Anonymous' 而不是 'Basic' 并且现在在末尾缺少 ", encoding="UTF-8"。
The HTTP request is unauthorized with client authentication scheme 'Anonymous'.
HTTP 请求未经授权,客户端身份验证方案为“匿名”。 The authentication header received from the server was 'Basic realm="Integration Server"'.
从服务器收到的身份验证标头是“基本领域=“集成服务器””。
Is the problem at my side or the servers?是我这边的问题还是服务器的问题?
Obviously my SOAP "skills" are greatly lacking now days, but I have just about tried every config combo I can think of with this new approach without luck.显然,我的 SOAP“技能”现在非常缺乏,但我几乎尝试了我能想到的所有配置组合,但没有运气。 Hope somebody can point me in the right direction.
希望有人能指出我正确的方向。
Ok this answer is for those who are trying to connect to a WCF service from a .net Core project.好的,这个答案适用于那些试图从 .net Core项目连接到 WCF服务的人。
Here is the solution to my problem, using the new .net Core WCF syntax/library.这是我的问题的解决方案,使用新的 .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);
}
UPDATE更新
I got the following exception using the code above我使用上面的代码得到以下异常
This OperationContextScope is being disposed out of order.
此 OperationContextScope 被无序处理。
Which seems to be something that is broken (or needs addressing) by the WCF team.这似乎是WCF 团队破坏(或需要解决)的问题。
So I had to do the following to make it work (based on this GitHub issue )因此,我必须执行以下操作才能使其正常工作(基于此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's 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
}
But your requirements will probably be different.但是您的要求可能会有所不同。 So here are the resources you might need to help you connecting to your WCF service are here:
因此,以下是帮助您连接到 WCF 服务可能需要的资源:
The tests helped me a lot but they where somewhat hard to find (I had help, thank you Zhenlan for answering my wcf github issue )这些测试对我帮助很大,但有些地方很难找到(我得到了帮助,感谢振兰回答我的 wcf github 问题)
To consume a SOAP service from .NET core, adding connected service from the project UI does not work.要从 .NET Core 使用 SOAP 服务,从项目 UI 添加连接的服务不起作用。
Option 1: Use dotnet-svcutil CLI.选项 1:使用 dotnet-svcutil CLI。 Prerequisite: VS 2017, Version 15.5 or above
先决条件:VS 2017,15.5或以上版本
Go to app.csproj file and add below references:转到 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>
Rebuild solution.重建解决方案。
Option 2 In case you need to refer more than one SOAP sevice,选项 2如果您需要参考多个 SOAP 服务,
So I had to do this and used the WCF Web Service Reference Provider Tool .所以我不得不这样做并使用了WCF Web Service Reference Provider Tool 。
The apparent need, according to responses like those here, for all the roundabout business with Bindings and Factories and Proxies seemed strange, considering that this all appeared to be part of the imported class.根据这里的回应,对于所有与绑定、工厂和代理有关的迂回业务的明显需求似乎很奇怪,考虑到这一切似乎都是导入类的一部分。
Not being able to find a straightforward official "HowTo", I will post my findings as to the simplest setup I was able to cobble together to fit my requirements with Digest authentication:无法找到一个简单的官方“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();
Now, if you don't need to do any authentication simply doing:现在,如果您不需要进行任何身份验证,只需执行以下操作:
ServiceName_PortClient client = new ServiceName_PortClient();
var result = await client.GetResultAsync();
Should be sufficient.应该够了。
The ServiceName_PortClient
class was generated as such by the import tool, where ServiceName
was the name of the service I was importing. ServiceName_PortClient
类是由导入工具生成的,其中ServiceName
是我要导入的服务的名称。
Of course it seems to be more in the spirit of the imported code to place the configuration in a partial ServiceName_PortClient
class along the lines of:当然,将配置放置在部分
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");
}
}
For those who are trying to do the same with NTLM and .Net Core and wondering what some of the variables are defined as, I clarified the code to look like:对于那些试图用 NTLM 和 .Net Core 做同样的事情并想知道一些变量被定义为什么的人,我澄清了代码如下:
IAService_PortType
is the service reference you created if you followed the guide on https://joshuachini.com/2017/07/13/calling-a-soap-service-from-asp-net-core-or-net-core/如果您遵循https://joshuachini.com/2017/07/13/calling-a-soap-service-from-asp-net-core-or-net-core/上的指南,则
IAService_PortType
是您创建的服务引用
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();
}
You can use the ChannelFactory
class even in pure dotnet core
.您甚至可以在纯
dotnet core
使用ChannelFactory
类。 It is fairly simple.这相当简单。
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();
You can find a working GitHub example here .您可以在此处找到一个可用的 GitHub 示例。
Don't forget to change between BasicHttpBinding
and BasicHttpsBinding
depending on whether you are using HTTP
or HTTPS
in the URL
.不要忘记根据您在
URL
中使用HTTP
还是HTTPS
在BasicHttpBinding
和BasicHttpsBinding
之间进行更改。
I had the same problem with There was an error reflecting 'login'
only in a unix (Docker and wsl2) environment.我遇到了同样的问题,仅在 unix(Docker 和 wsl2)环境中
There was an error reflecting 'login'
。 On Windows there was no problem at all.在 Windows 上根本没有问题。
I tried to create the connected service via the dotnet-svcutil
(version 2.0.1), but this didn't solve the issue.我尝试通过
dotnet-svcutil
(版本 2.0.1)创建连接的服务,但这并没有解决问题。
I finally managed to find the root of my problem.我终于找到了问题的根源。 I published my application with the following dotnet command:
dotnet publish "myapp/myapp.csproj" -c Release -r linux-x64 --self-contained true -p:PublishTrimmed=true -p:ReadyToRun=true -p:ReadyToRunShowWarnings=true -o publish
我使用以下 dotnet 命令发布了我的应用程序:
dotnet publish "myapp/myapp.csproj" -c Release -r linux-x64 --self-contained true -p:PublishTrimmed=true -p:ReadyToRun=true -p:ReadyToRunShowWarnings=true -o publish
When I removed the attribute -p:PublishTrimmed=true
it finally worked both under wsl2 and in my docker environment.当我删除属性
-p:PublishTrimmed=true
它最终在 wsl2 和我的 docker 环境中都可以工作。
I used the .net core 3 svutil to generate the wrapper classes from an old school asmx SOAP / WSDL.我使用 .net core 3 svutil 从老式的 asmx SOAP/WSDL 生成包装类。 See - https://docs.microsoft.com/en-us/dotnet/core/additional-tools/dotnet-svcutil-guide?tabs=dotnetsvcutil2x
请参阅 - https://docs.microsoft.com/en-us/dotnet/core/additional-tools/dotnet-svcutil-guide?tabs=dotnetsvcutil2x
I then wrapped the remote and used the code below to set the authentication.然后我包装了遥控器并使用下面的代码来设置身份验证。 I believe this works for Kerberos and will fall back to NTLM (depending on the headers returned from the server).
我相信这适用于 Kerberos 并且会回退到 NTLM(取决于从服务器返回的标头)。
public class DocumentManagerWrapper
{
public DocumentManagerWrapper(string serviceUrl,
String username,
String password)
{
_serviceUrl = serviceUrl;
_username = username;
_password = password;
}
private String _serviceUrl;
private String _username;
private String _password;
private DocumentManagerSoap GetSoapConnection()
{
BasicHttpSecurityMode mode = _serviceUrl.StartsWith("https") ? BasicHttpSecurityMode.Transport : BasicHttpSecurityMode.TransportCredentialOnly;
BasicHttpBinding binding = new BasicHttpBinding(mode);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
EndpointAddress address = new EndpointAddress(_serviceUrl);
ChannelFactory<DocumentManagerSoapChannel> channel = new ChannelFactory<DocumentManagerSoapChannel>(binding, address);
channel.Credentials.Windows.ClientCredential.UserName = _username;
channel.Credentials.Windows.ClientCredential.Password = _password;
DocumentManagerSoap soap = channel.CreateChannel();
return soap;
}
}
Note - DocumentManagerSoapChannel and DocumentManagerSoap classes are generated by svcutil.注 - DocumentManagerSoapChannel和DocumentManagerSoap类由 svcutil 生成。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.