web-dev-qa-db-fra.com

Appel d'un service SOAP dans .net Core

Je porte un code .net 4.6.2 sur un projet . Net Core , qui appelle un service SOAP. Dans le nouveau code, je me sers de C # (pour des raisons de configuration, je ne me souviens tout simplement pas pourquoi pour le moment).

Mais je reçois l'exception suivante.

Une erreur s'est produite lors de la réception de la réponse HTTP à https://someurl.com/ws/Thing.pub.ws:Something . Cela peut être dû au fait que la liaison de point de terminaison de service n'utilise pas le protocole HTTP. Cela peut également être dû à un contexte de requête HTTP interrompu par le serveur (probablement en raison de l'arrêt du service). Voir les journaux du serveur pour plus de détails.

Le code qui le jette est

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);
}

Basé sur l'ancienne config où cela fonctionne, appeler le service est comme ceci, que me manque-t-il ?

<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>

Je suis simplement incapable de trouver beaucoup d'informations sur ce site en ligne. J'espère que vous pourrez m'aider.

UPDATE Selon la suggestion de Sixto Saez, j'ai le noeud final pour révéler son erreur et c'est

La demande HTTP n'est pas autorisée avec le schéma d'authentification client 'Basic'. L'en-tête d'authentification reçu du serveur était 'Basic realm = "Integration Server", encoding = "UTF-8"'.

Je vais essayer de savoir quoi faire et poster le résultat ici en cas de succès.

UPDATE 2

Ok maintenant j'ai essayé de passer à la nouvelle syntaxe avec ce code ici

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;
}

mais je reçois toujours la même erreur (légèrement différente). Il a maintenant "Anonyme" au lieu de "Basique" et manque maintenant ", encoding =" UTF-8 "à la fin.

La requête HTTP n'est pas autorisée avec le schéma d'authentification client 'Anonymous'. L'en-tête d'authentification reçu du serveur était 'Basic realm = "Integration Server"'.

Le problème vient-il de moi ou des serveurs?

Évidemment, mes SOAP _ _ manquent cruellement, mais j’ai presque essayé tous les combos de configuration auxquels je peux penser avec cette nouvelle approche sans chance. J'espère que quelqu'un pourra me diriger dans la bonne direction.

20
Sturla

Ok cette réponse est pour ceux qui essaient de se connecter à un service WCF à partir d'un noyau .net projet.

Voici la solution à mon problème en utilisant la nouvelle syntaxe/bibliothèque .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

J'ai eu l'exception suivante en utilisant le code ci-dessus

OperationContextScope est mis hors service.

Ce qui semble être quelque chose qui est cassé (ou doit être corrigé) par l'équipe de la WCF.

Donc je devais faire ce qui suit pour le faire fonctionner (basé sur ceci question de 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
}

Mais vos exigences seront probablement différentes. Voici donc les ressources dont vous pourriez avoir besoin pour vous aider à vous connecter à votre service WCF:

Les tests m'ont beaucoup aidé, mais ils étaient un peu difficiles à trouver (j'ai eu de l'aide, merci Zhenlan pour répondant à mon problème de wcf github )

23
Sturla

Pour utiliser un service SOAP à partir du noyau .NET, l'ajout d'un service connecté à partir de l'interface utilisateur du projet ne fonctionne pas.

Option 1: Utilisez la CLI dotnet-svcutil. Prérequis: VS 2017, version 15.5 ou supérieure

  1. Lancez l'invite de commande de développeur VS 2017.
  2. Allez dans le fichier app.csproj et ajoutez les références ci-dessous:

    <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. Reconstruire la solution.

  4. Modifiez le répertoire à l’emplacement de votre projet à partir de la commande VS Invite.
  5. exécutez la commande: svcutil SOAP_URL? wsdl; exemple: exemple.com/test/testing?wsdl Ceci générera des fichiers de référence et un fichier output.config dans votre projet.
  6. .Net Core ne contient aucun fichier app.config ou web.config, mais le fichier output.config servira la liaison SOAP.

Option 2 Dans le cas où vous devez référencer plus d'un SOAP sevice,

  1. Créez un nouveau projet de bibliothèque de classes, utilisez .NET Framework 4.5.1. NET Framework a de l’importance, car j’ai bien vu que les fichiers de référence générés à partir du contrat ne sont pas corrects si .Net Framework est la plus récente.
  2. Ajouter une référence de service par un clic droit sur Références.
  3. Reportez-vous au projet de bibliothèque de classe à partir de votre projet principal .Net.
4
Palash Roy

Pour ceux qui essaient de faire la même chose avec NTLM et .Net Core et se demandant en quoi certaines des variables sont définies, j'ai clarifié le code pour qu'il ressemble à ceci:

IAService_PortType est la référence de service que vous avez créée si vous avez suivi le guide sur https://joshuachini.com/2017/07/13/calling-a-soso-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();
}
2
C64

Je devais donc faire cela et utiliser le Outil de fournisseur de référence de service Web WCF .

Le besoin apparent, selon des réponses comme celles d’ici, de faire le tour de table avec Bindings and Factories and Proxies semblait étrange, étant donné que tout cela semblait faire partie de la classe importée.

Ne pouvant pas trouver un simple "HowTo" officiel, je publierai mes conclusions sur la configuration la plus simple que j'ai pu concocter pour répondre à mes exigences. avec authentification Digest:

    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();

Maintenant, si vous n'avez pas besoin de faire d'authentification, faites simplement:

    ServiceName_PortClient client = new ServiceName_PortClient();
    var result = await client.GetResultAsync();

Devrait être suffisant.

La classe ServiceName_PortClient a été générée en tant que telle par l'outil d'importation, où ServiceName était le nom du service que j'importais.

Bien sûr, il semble être plus dans l’esprit du code importé de placer la configuration dans une classe partielle ServiceName_PortClient comme suit:

    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");
        }
    }
0
David