web-dev-qa-db-fra.com

REST / SOAP points de terminaison pour un service WCF

J'ai un service WCF et je souhaite l'exposer à la fois comme service RESTful et comme service SOAP. Quelqu'un a déjà fait quelque chose comme ça auparavant?

413
Wessam Zeidan

Vous pouvez exposer le service à deux points de terminaison différents . Le SOAP peut utiliser la liaison qui prend en charge SOAP, par exemple. basicHttpBinding, le RESTful peut utiliser le webHttpBinding. Je suppose que votre service REST sera en JSON. Dans ce cas, vous devez configurer les deux ordinateurs d'extrémité avec la configuration de comportement suivante.

<endpointBehaviors>
  <behavior name="jsonBehavior">
    <enableWebScript/>
  </behavior>
</endpointBehaviors>

Un exemple de configuration de point de terminaison dans votre scénario est 

<services>
  <service name="TestService">
    <endpoint address="soap" binding="basicHttpBinding" contract="ITestService"/>
    <endpoint address="json" binding="webHttpBinding"  behaviorConfiguration="jsonBehavior" contract="ITestService"/>
  </service>
</services>

oui, le service sera disponible à

Appliquez [WebGet] au contrat d'exploitation pour le rendre RESTful .

public interface ITestService
{
   [OperationContract]
   [WebGet]
   string HelloWorld(string text)
}

Notez que si le service REST n'est pas en JSON, les paramètres des opérations ne peuvent pas contenir de type complexe.

Répondre au message pour SOAP et RESTful POX (XML)

Pour un ancien format XML comme format de retour, il s'agit d'un exemple qui fonctionnerait à la fois pour SOAP et XML.

[ServiceContract(Namespace = "http://test")]
public interface ITestService
{
    [OperationContract]
    [WebGet(UriTemplate = "accounts/{id}")]
    Account[] GetAccount(string id);
}

Comportement POX pour REST Plain Old XML

<behavior name="poxBehavior">
  <webHttp/>
</behavior>

Points de terminaison

<services>
  <service name="TestService">
    <endpoint address="soap" binding="basicHttpBinding" contract="ITestService"/>
    <endpoint address="xml" binding="webHttpBinding"  behaviorConfiguration="poxBehavior" contract="ITestService"/>
  </service>
</services>

Le service sera disponible à

REST demande Essayez-le dans le navigateur,

http://www.example.com/xml/accounts/A123

SOAP demande la configuration du point de terminaison client Client pour le service SOAP après l'ajout de la référence de service,

  <client>
    <endpoint address="http://www.example.com/soap" binding="basicHttpBinding"
      contract="ITestService" name="BasicHttpBinding_ITestService" />
  </client>

en C #

TestServiceClient client = new TestServiceClient();
client.GetAccount("A123");

Une autre façon de le faire est d'exposer deux contrats de service différents, chacun avec une configuration spécifique. Cela peut générer des doublons au niveau du code, mais en fin de compte, vous souhaitez le faire fonctionner.

575
Ray Lu

Ce message a déjà une très bonne réponse de "Wiki de la communauté" et je recommande également de consulter le blog de Rick Strahl, il existe de nombreux bons messages sur WCF Rest tels que ceci .

J'ai utilisé les deux pour obtenir ce type de service MyService ... Ensuite, je peux utiliser l'interface REST de jQuery ou SOAP de Java.

Ceci est de mon Web.Config:

<system.serviceModel>
 <services>
  <service name="MyService" behaviorConfiguration="MyServiceBehavior">
   <endpoint name="rest" address="" binding="webHttpBinding" contract="MyService" behaviorConfiguration="restBehavior"/>
   <endpoint name="mex" address="mex" binding="mexHttpBinding" contract="MyService"/>
   <endpoint name="soap" address="soap" binding="basicHttpBinding" contract="MyService"/>
  </service>
 </services>
 <behaviors>
  <serviceBehaviors>
   <behavior name="MyServiceBehavior">
    <serviceMetadata httpGetEnabled="true"/>
    <serviceDebug includeExceptionDetailInFaults="true" />
   </behavior>
  </serviceBehaviors>
  <endpointBehaviors>
   <behavior name="restBehavior">
    <webHttp/>
   </behavior>
  </endpointBehaviors>
 </behaviors>
</system.serviceModel>

Et voici ma classe de service (.svc-codebehind, aucune interface requise):

    /// <summary> MyService documentation here ;) </summary>
[ServiceContract(Name = "MyService", Namespace = "http://myservice/", SessionMode = SessionMode.NotAllowed)]
//[ServiceKnownType(typeof (IList<MyDataContractTypes>))]
[ServiceBehavior(Name = "MyService", Namespace = "http://myservice/")]
public class MyService
{
    [OperationContract(Name = "MyResource1")]
    [WebGet(ResponseFormat = WebMessageFormat.Xml, UriTemplate = "MyXmlResource/{key}")]
    public string MyResource1(string key)
    {
        return "Test: " + key;
    }

    [OperationContract(Name = "MyResource2")]
    [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "MyJsonResource/{key}")]
    public string MyResource2(string key)
    {
        return "Test: " + key;
    }
}

En fait, je n'utilise que Json ou XML, mais ces deux-là sont ici à des fins de démonstration. Ce sont des requêtes GET pour obtenir des données. Pour insérer des données, j'utiliserais une méthode avec des attributs:

[OperationContract(Name = "MyResourceSave")]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, UriTemplate = "MyJsonResource")]
public string MyResourceSave(string thing){
    //...
38
Tuomas Hietanen

Si vous souhaitez uniquement développer un seul service Web et le faire héberger sur de nombreux points de terminaison différents (c'est-à-dire SOAP + REST, avec XML, JSON, CSV, HTML outputes). Vous devez également envisager d’utiliser ServiceStack, que j’ai construit exactement à cet effet, où chaque service que vous développez est automatiquement disponible sur les noeuds finaux SOAP et REST. the-box sans aucune configuration requise.

Le Hello World exemple montre comment créer un service simple avec simplement (aucune configuration requise):

public class Hello {
    public string Name { get; set; }
}

public class HelloResponse {
    public string Result { get; set; }
}

public class HelloService : IService
{
    public object Any(Hello request)
    {
        return new HelloResponse { Result = "Hello, " + request.Name };
    }
}

Aucune autre configuration n'est requise et ce service est immédiatement disponible avec REST dans:

Il intègre également une sortie HTML conviviale (lorsqu'il est appelé avec un client HTTP doté de Accept: text/html, par exemple un navigateur), ce qui vous permet de mieux visualiser la sortie de vos services. .

La gestion de différents REST verbes est également aussi simple. Voici une application CRUD de service REST complète dans une page de C # (moins qu'il n'en faudrait pour configurer WCF;):

24
mythz

MSDN semble avoir un article pour cela maintenant: 

https://msdn.Microsoft.com/en-us/library/bb412196(v=vs.110).aspx

Intro: 

Par défaut, Windows Communication Foundation (WCF) rend les ordinateurs d'extrémité disponibles uniquement pour les clients SOAP. Dans Comment: créer un service HTTP Web WCF de base, un point de terminaison est mis à la disposition des clients non SOAP. Il peut arriver que vous souhaitiez rendre le même contrat disponible dans les deux sens, en tant que point de terminaison Web et en tant que point de terminaison SOAP. Cette rubrique présente un exemple de procédure à suivre.

6
FMFF

Nous devons définir la configuration du comportement sur REST endpoint

<endpointBehaviors>
  <behavior name="restfulBehavior">
   <webHttp defaultOutgoingResponseFormat="Json" defaultBodyStyle="Wrapped" automaticFormatSelectionEnabled="False" />
  </behavior>
</endpointBehaviors>

et aussi à un service 

<serviceBehaviors>
   <behavior>
     <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="false" />
   </behavior>
</serviceBehaviors>

Après les comportements, l'étape suivante est la liaison. Par exemple, basicHttpBinding vers SOAP extrémité et webHttpBinding vers RESTE.

<bindings>
   <basicHttpBinding>
     <binding name="soapService" />
   </basicHttpBinding>
   <webHttpBinding>
     <binding name="jsonp" crossDomainScriptAccessEnabled="true" />
   </webHttpBinding>
</bindings>

Enfin, nous devons définir le point d'extrémité 2 dans la définition de service. Attention pour l'adresse = "" du noeud final, où le service REST n'est pas nécessaire rien.

<services>
  <service name="ComposerWcf.ComposerService">
    <endpoint address="" behaviorConfiguration="restfulBehavior" binding="webHttpBinding" bindingConfiguration="jsonp" name="jsonService" contract="ComposerWcf.Interface.IComposerService" />
    <endpoint address="soap" binding="basicHttpBinding" name="soapService" contract="ComposerWcf.Interface.IComposerService" />
    <endpoint address="mex" binding="mexHttpBinding" name="metadata" contract="IMetadataExchange" />
  </service>
</services>

Dans Interface du service, nous définissons l'opération avec ses attributs. 

namespace ComposerWcf.Interface
{
    [ServiceContract]
    public interface IComposerService
    {
        [OperationContract]
        [WebInvoke(Method = "GET", UriTemplate = "/autenticationInfo/{app_id}/{access_token}", ResponseFormat = WebMessageFormat.Json,
            RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
        Task<UserCacheComplexType_RootObject> autenticationInfo(string app_id, string access_token);
    }
}

En rejoignant toutes les parties, ce sera notre définition WCF system.serviceModel.

<system.serviceModel>

  <behaviors>
    <endpointBehaviors>
      <behavior name="restfulBehavior">
        <webHttp defaultOutgoingResponseFormat="Json" defaultBodyStyle="Wrapped" automaticFormatSelectionEnabled="False" />
      </behavior>
    </endpointBehaviors>
    <serviceBehaviors>
      <behavior>
        <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
        <serviceDebug includeExceptionDetailInFaults="false" />
      </behavior>
    </serviceBehaviors>
  </behaviors>

  <bindings>
    <basicHttpBinding>
      <binding name="soapService" />
    </basicHttpBinding>
    <webHttpBinding>
      <binding name="jsonp" crossDomainScriptAccessEnabled="true" />
    </webHttpBinding>
  </bindings>

  <protocolMapping>
    <add binding="basicHttpsBinding" scheme="https" />
  </protocolMapping>

  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />

  <services>
    <service name="ComposerWcf.ComposerService">
      <endpoint address="" behaviorConfiguration="restfulBehavior" binding="webHttpBinding" bindingConfiguration="jsonp" name="jsonService" contract="ComposerWcf.Interface.IComposerService" />
      <endpoint address="soap" binding="basicHttpBinding" name="soapService" contract="ComposerWcf.Interface.IComposerService" />
      <endpoint address="mex" binding="mexHttpBinding" name="metadata" contract="IMetadataExchange" />
    </service>
  </services>

</system.serviceModel>

Pour tester les deux terminaux, nous pouvons utiliser WCFClient pour SOAP et PostMan pour RESTE.

2
Jailson Evora

C'est ce que j'ai fait pour que ça marche. Assurez-vous de mettre
webHttp automaticFormatSelectionEnabled = "true" à l'intérieur du comportement du noeud final.

[ServiceContract]
public interface ITestService
{

    [WebGet(BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "/product", ResponseFormat = WebMessageFormat.Json)]
    string GetData();
}

public class TestService : ITestService
{
    public string GetJsonData()
    {
        return "I am good...";
    }
}

Modèle de service intérieur

   <service name="TechCity.Business.TestService">

    <endpoint address="soap" binding="basicHttpBinding" name="SoapTest"
      bindingName="BasicSoap" contract="TechCity.Interfaces.ITestService" />
    <endpoint address="mex"
              contract="IMetadataExchange" binding="mexHttpBinding"/>
    <endpoint behaviorConfiguration="jsonBehavior" binding="webHttpBinding"
              name="Http" contract="TechCity.Interfaces.ITestService" />
    <Host>
      <baseAddresses>
        <add baseAddress="http://localhost:8739/test" />
      </baseAddresses>
    </Host>
  </service>

Comportement du point final

  <endpointBehaviors>
    <behavior name="jsonBehavior">
      <webHttp automaticFormatSelectionEnabled="true"  />
      <!-- use JSON serialization -->
    </behavior>
  </endpointBehaviors>
0