[英]Calling a SOAP service in .net Core
我正在將 .net 4.6.2 代碼移植到調用 SOAP 服務的.net Core 項目。 在新代碼中,我使用 C#(由於某些配置原因,我現在不記得為什么了)。
但我收到以下異常。
接收對https://someurl.com/ws/Thing.pub.ws:Something的 HTTP 響應時出錯。 這可能是由於服務端點綁定未使用 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>
我只是無法在網上找到很多關於此的信息。 希望你能幫助我。
根據 Sixto Saez 的建議更新我得到了端點來揭示它的錯誤,它是
HTTP 請求未經授權,客戶端身份驗證方案為“基本”。 從服務器收到的身份驗證標頭是“基本領域=“集成服務器”,編碼=“UTF-8””。
如果成功,我會嘗試找出該怎么做並在此處發布結果。
更新 2
好的,現在我嘗試使用此代碼移至新語法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;
}
但我仍然得到相同(略有不同)的錯誤。 它現在有 'Anonymous' 而不是 'Basic' 並且現在在末尾缺少 ", encoding="UTF-8"。
HTTP 請求未經授權,客戶端身份驗證方案為“匿名”。 從服務器收到的身份驗證標頭是“基本領域=“集成服務器””。
是我這邊的問題還是服務器的問題?
顯然,我的 SOAP“技能”現在非常缺乏,但我幾乎嘗試了我能想到的所有配置組合,但沒有運氣。 希望有人能指出我正確的方向。
好的,這個答案適用於那些試圖從 .net Core項目連接到 WCF服務的人。
這是我的問題的解決方案,使用新的 .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 被無序處理。
因此,我必須執行以下操作才能使其正常工作(基於此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
}
但是您的要求可能會有所不同。 因此,以下是幫助您連接到 WCF 服務可能需要的資源:
這些測試對我幫助很大,但有些地方很難找到(我得到了幫助,感謝振蘭回答我的 wcf github 問題)
要從 .NET Core 使用 SOAP 服務,從項目 UI 添加連接的服務不起作用。
選項 1:使用 dotnet-svcutil CLI。 先決條件:VS 2017,15.5或以上版本
轉到 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>
重建解決方案。
選項 2如果您需要參考多個 SOAP 服務,
所以我不得不這樣做並使用了WCF Web Service Reference Provider Tool 。
根據這里的回應,對於所有與綁定、工廠和代理有關的迂回業務的明顯需求似乎很奇怪,考慮到這一切似乎都是導入類的一部分。
無法找到一個簡單的官方“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");
}
}
對於那些試圖用 NTLM 和 .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();
}
您甚至可以在純dotnet core
使用ChannelFactory
類。 這相當簡單。
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 示例。
不要忘記根據您在URL
中使用HTTP
還是HTTPS
在BasicHttpBinding
和BasicHttpsBinding
之間進行更改。
我遇到了同樣的問題,僅在 unix(Docker 和 wsl2)環境中There was an error reflecting 'login'
。 在 Windows 上根本沒有問題。
我嘗試通過dotnet-svcutil
(版本 2.0.1)創建連接的服務,但這並沒有解決問題。
我終於找到了問題的根源。 我使用以下 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
當我刪除屬性-p:PublishTrimmed=true
它最終在 wsl2 和我的 docker 環境中都可以工作。
我使用 .net core 3 svutil 從老式的 asmx SOAP/WSDL 生成包裝類。 請參閱 - https://docs.microsoft.com/en-us/dotnet/core/additional-tools/dotnet-svcutil-guide?tabs=dotnetsvcutil2x
然后我包裝了遙控器並使用下面的代碼來設置身份驗證。 我相信這適用於 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;
}
}
注 - DocumentManagerSoapChannel和DocumentManagerSoap類由 svcutil 生成。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.