56

I´m porting a .net 4.6.2 code to a .net Core project, that calls a SOAP service. In the new code I´m using C# (because of some config reasons I just can´t remember why right now).

But I´m getting the following exception.

An error occurred while receiving the HTTP response to https://someurl.com/ws/Thing.pub.ws:Something. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). 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

The HTTP request is unauthorized with client authentication scheme 'Basic'. The authentication header received from the server was 'Basic realm="Integration Server", encoding="UTF-8"'.

I´ll try to find out what to do and post the result here if successful.

UPDATE 2

Ok now I tried to move to the new syntax with this code 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.

The HTTP request is unauthorized with client authentication scheme 'Anonymous'. 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. Hope somebody can point me in the right direction.

Sturla
  • 3,446
  • 3
  • 39
  • 56
  • 2
    The error seems like a server-side issue. On the client-side, it is not clear if the `Thing.Authorization_PortTypeClient` class encapsulates creation of a `ChannelFactory` and generation of a `Channel` instance. See this [test code](https://github.com/dotnet/wcf/blob/master/src/System.Private.ServiceModel/tests/Scenarios/Binding/Http/BasicHttpBindingTests.4.0.0.cs) for how to create and call a WCF client in .NET Core. This [GitHub repo for WCF client libraries](https://github.com/dotnet/wcf/) may contain what you are looking for, documentation-wise. – Sixto Saez Feb 05 '18 at 15:35
  • Thank you @SixtoSaez. I updated the question. But now I get another error I just can´t find out what really means. I have contacted the server people to chek if there is something wrong on their side but if you have any insights I would appreciate all you have to give. – Sturla Feb 06 '18 at 15:47
  • Check this answer https://stackoverflow.com/a/52696777/5077196 – Yahya Hussein Oct 08 '18 at 06:48

7 Answers7

48

Ok this answer is for those who are trying to connect to a WCF service from a .net Core project.

Here is the solution to my problem, using the new .net Core WCF syntax/library.

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.

Which seems to be something that is broken (or needs addressing) by the WCF team.

So I had to do the following to make it work (based on this GitHub issue)

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:

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)

Doug
  • 32,844
  • 38
  • 166
  • 222
Sturla
  • 3,446
  • 3
  • 39
  • 56
  • 2
    what is IAService? – Yahya Hussein Oct 01 '18 at 12:34
  • I-A-Service is just an interface with service model contracts that describe the WCF service take a look at https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-use-the-channelfactory – Sturla Oct 04 '18 at 10:38
  • 5
    For anyone else that finds this while searching for a solution. The interface IAService_PortType mentioned @Sturla is the interface that matches your webservice. If you follow this guide to generate https://joshuachini.com/2017/07/13/calling-a-soap-service-from-asp-net-core-or-net-core/ - the interface will exists inside the namespace that you specified during the generation. – Mötz Nov 15 '18 at 10:06
  • I am getting following Error Any please: There was an error in serializing body of message : 'There was an error generating the XML document.'. Please see InnerException for more details. – Sharad Dec 23 '21 at 12:57
7

To consume a SOAP service from .NET core, adding connected service from the project UI does not work.

Option 1: Use dotnet-svcutil CLI. Prerequisite: VS 2017, Version 15.5 or above

  1. Launch Developer Command Prompt VS 2017.
  2. Go to app.csproj file and add below references:

    <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. Rebuild solution.

  4. Change directory to your project location from VS command prompt.
  5. run command: svcutil SOAP_URL?wsdl ; example: example.com/test/testing?wsdl This will generate reference files and output.config file in your project.
  6. .Net Core does not have any app.config or web.config files, but the output.config file will serve the SOAP binding.

Option 2 In case you need to refer more than one SOAP sevice,

  1. Create a new class library project, use .Net framework 4.5.1 .Net framework matters as i saw the reference files generated from contract is not correct if .Net Framework is latest.
  2. Add service reference by right click on References.
  3. Refer the class library project from your .Net core project.
Palash Roy
  • 1,547
  • 1
  • 15
  • 11
  • I'm pretty sure I used this extension to do t https://learn.microsoft.com/en-us/dotnet/core/additional-tools/wcf-web-service-reference-guide – Sturla Dec 07 '18 at 13:23
  • 1
    What if it's a .NET Standard class library? Microsoft.AspNetCore.All can't be added in that case... – Justin Jan 17 '19 at 15:31
  • @Justin , for your case option 2 mentioned above is applicable. – Palash Roy Jul 17 '19 at 16:48
  • `new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential)` this won't work and the problem is making connection over WCF with TransportWithMessageCredential. – uzay95 Dec 16 '19 at 08:38
  • Version was updated and 4.6.0 is working very good with TransportWithMessageCredential ` ` – uzay95 Dec 16 '19 at 08:51
  • 1
    The Option 1 worked for me. I had to look for a way to access the service by using var p = new ServiceName(new BasicHttpBinding(), new EndpointAddress("ur service url")); – israel Feb 27 '20 at 09:12
  • Option 2 does not work for wshttpbinding bindings in .NET Core – AussieJoe Mar 18 '20 at 22:02
  • Hi, I'm trying Option 2 and getting this exceptions "Only HOST and HTTP service principal names are supported System.InvalidOperationException".i'm using var upnEndpointIdentity = EndpointIdentity.CreateUpnIdentity(userPrincipalName) – NAS Jul 14 '21 at 14:49
  • var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly); binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm; binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Ntlm; – NAS Jul 14 '21 at 14:51
6

So I had to do this and used the 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:

    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.

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:

    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");
        }
    }
David
  • 61
  • 1
  • 2
5

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:

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/

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();
}
renemadsen
  • 161
  • 1
  • 9
4

You can use the ChannelFactoryclass even in pure dotnet core. 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.

Don't forget to change between BasicHttpBinding and BasicHttpsBinding depending on whether you are using HTTP or HTTPS in the URL.

Michael
  • 397
  • 2
  • 10
2

I had the same problem with There was an error reflecting 'login' only in a unix (Docker and wsl2) environment. On Windows there was no problem at all.

I tried to create the connected service via the dotnet-svcutil (version 2.0.1), but this didn't solve the issue.

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

When I removed the attribute -p:PublishTrimmed=true it finally worked both under wsl2 and in my docker environment.

rogaa
  • 370
  • 2
  • 16
1

I used the .net core 3 svutil to generate the wrapper classes from an old school asmx SOAP / WSDL. See - https://learn.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).

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.

Ciarán Bruen
  • 5,221
  • 13
  • 59
  • 69
stuartm9999
  • 139
  • 1
  • 8