在.net Core中调用SOAP服务

Stu*_*rla 23 c# wcf soap .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);
}
Run Code Online (Sandbox Code Playgroud)

基于旧的配置工作调用服务是这样的,我错过了什么?

<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>
Run Code Online (Sandbox Code Playgroud)

我无法在网上找到很多这方面的信息.希望您能够帮助我.

更新 Per Sixto Saez的建议我得到了端点以显示其错误,它是

HTTP请求未经授权使用客户端身份验证方案"Basic".从服务器接收的认证头是'Basic realm ="Integration Server",encoding ="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;
}
Run Code Online (Sandbox Code Playgroud)

但我仍然得到相同(略有不同)的错误.它现在有'Anonymous'而不是'Basic',现在最后缺少",encoding ="UTF-8".

HTTP请求未经授权,客户端身份验证方案为"匿名".从服务器收到的身份验证标头是'Basic realm ="Integration Server"'.

问题出在我身边还是服务器上?

显然,我的SOAP"技能"现在非常缺乏,但我只是尝试了一下我能用这种新方法想到的每个配置组合而没有运气.希望有人能指出我正确的方向.

Stu*_*rla 24

好的,这个答案适用于那些试图从.net Core项目连接到WCF服务的人.

这是我的问题的解决方案,使用新的.net核心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);
}
Run Code Online (Sandbox Code Playgroud)

UPDATE

我使用上面的代码得到以下异常

此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'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
}
Run Code Online (Sandbox Code Playgroud)

但您的要求可能会有所不同.以下是帮助您连接到WCF服务可能需要的资源:

这些测试给了我很多帮助,但是有些难以找到(我有帮助,感谢Zhenlan 回答我的wcf github问题)

  • 对于在搜索解决方案时发现这一点的任何其他人.提到的IAService_PortType接口@Sturla是与您的Web服务匹配的接口.如果您按照本指南生成https://joshuachini.com/2017/07/13/calling-a-soap-service-from-asp-net-core-or-net-core/ - 界面将存在于您在生成期间指定的命名空间. (4认同)
  • 什么是IAService? (2认同)

Pal*_*Roy 6

要从 .NET Core 使用 SOAP 服务,从项目 UI 添加连接的服务不起作用。

选项 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>
    
    Run Code Online (Sandbox Code Playgroud)
  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 核心项目中的类库项目。


ren*_*sen 5

对于那些尝试对 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();
}
Run Code Online (Sandbox Code Playgroud)


小智 5

所以我不得不这样做并使用了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();
Run Code Online (Sandbox Code Playgroud)

现在,如果您不需要进行任何身份验证,只需执行以下操作:

    ServiceName_PortClient client = new ServiceName_PortClient();
    var result = await client.GetResultAsync();
Run Code Online (Sandbox Code Playgroud)

应该够了。

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");
        }
    }
Run Code Online (Sandbox Code Playgroud)