Endpoint REST / SOAP per un servizio WCF


425

Ho un servizio WCF e voglio esporlo sia come servizio RESTfull che come servizio SOAP. Qualcuno ha fatto qualcosa di simile prima?


bella domanda e grandi risposte.
chandra rv,

Risposte:


584

È possibile esporre il servizio in due diversi endpoint. quello SOAP può usare l'associazione che supporta SOAP, ad esempio basicHttpBinding, quello RESTful può usare webHttpBinding. Presumo che il servizio REST sarà in JSON, in tal caso, è necessario configurare i due endpoint con la seguente configurazione comportamentale

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

Un esempio di configurazione dell'endpoint nel tuo scenario è

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

quindi, il servizio sarà disponibile all'indirizzo

Applicare [WebGet] al contratto di operazione per renderlo RESTful. per esempio

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

Nota, se il servizio REST non è in JSON, i parametri delle operazioni non possono contenere tipi complessi.

Rispondi al post per SOAP e RESTful POX (XML)

Per il semplice vecchio XML come formato di ritorno, questo è un esempio che funzionerebbe sia per SOAP che per XML.

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

Comportamento POX per REST Plain Old XML

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

endpoint

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

Il servizio sarà disponibile all'indirizzo

Richiesta REST provalo nel browser,

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

Configurazione dell'endpoint del client richiesta SOAP per il servizio SOAP dopo aver aggiunto il riferimento al servizio,

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

in C #

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

Un altro modo per farlo è quello di esporre due diversi contratti di servizio e ognuno con una configurazione specifica. Questo può generare alcuni duplicati a livello di codice, tuttavia alla fine della giornata, vuoi farlo funzionare.


11
Come appare quando ho .svc ospitato in IIS in una directory virtuale come someserver / myvirtualdir / service.svc ? Come dovrei accedervi?
Sunny Milenov,

Vorrei fare un ulteriore passo avanti e aggiungere un'associazione a HTTPS per l'indirizzo JSON. Come lo faccio? stackoverflow.com/questions/18213472/…
Steve

Sta dicendo che il mio contratto IEvents non è valido quando provo a fare riferimento alla mia interfaccia di servizio: <nome servizio = "Eventi"> <indirizzo endpoint = "json" binding = "webHttpBinding" behaviorConfiguration = "jsonBehavior" contract = "IEvents" />. My IEvents ha un attributo [ServiceContract] sull'interfaccia, quindi non so perché. </service>
Positivo

Posso ottenere localhost: 44652 / MyResource / json per funzionare ma non riesco a ottenere un id per funzionare localhost: 44652 / MyResource / 98 / json . Ho provato ad aggiungere un UriTemplate di "/ {id}", ho anche provato "events / {id} ma non lo trovo quando provo a colpire il servizio. Solo il primo funziona, non sono sicuro di come ottenere il secondo per lavorare
Acquista Positivo

2
Come può funzionare senza file fisico lì? Mi sembra di avere 404 errori, deve mancare qualcosa
RoboJ1M,

39

Questo post ha già un'ottima risposta da "Community wiki" e consiglio anche di consultare il Blog Web di Rick Strahl, ci sono molti buoni post su WCF Rest come questo .

Ho usato entrambi per ottenere questo tipo di servizio MyService ... Quindi posso usare l'interfaccia REST da jQuery o SOAP da Java.

Questo è dal mio 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>

E questa è la mia classe di servizio (.svc-codebehind, nessuna interfaccia richiesta):

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

In realtà uso solo Json o Xml ma entrambi sono qui per uno scopo dimostrativo. Quelle sono richieste GET per ottenere dati. Per inserire dati vorrei usare il metodo con gli attributi:

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

Sono curioso di sapere quali vantaggi ritieni di ottenere aggiungendo questi attributi WebGet e WebInvoke.
Darrel Miller,

2
È possibile effettuare richieste tramite browser: localhost / MyService.svc / MyXmlResource / test E pronunciare esplicitamente il formato Json o Xml. Se vuoi che gli stessi metodi rispondano ad entrambi, ecco un link: blogs.msdn.com/dotnetinterop/archive/2008/11/04/…
Tuomas Hietanen,

Questo è a scopo di test. Solo per vedere se i tuoi endpoint funzionano. Hai visto SoapUI? soapui.org
Darrel Miller,

@TuomasHietanen - Non ottengo la risposta di tipo JSON usando il comportamento webHttp, tuttavia usando abilitazione Web ricevo una risposta di tipo JSON. Ho inserito ResponseFormat come WebMessageFormat.Json. D'altra parte non posso usare URItemplate se uso il comportamento enableWebScript. Qualche idea?
smile.al.d.way

1
@CoffeeAddict - Perché dovresti usare inteface? Solo per avere l'interfaccia? Non riutilizzerai mai questa interfaccia. Questo è più semplice
Tuomas Hietanen,

25

Se si desidera sviluppare un solo servizio Web e averlo ospitato su molti endpoint diversi (ad esempio SOAP + REST, con output XML, JSON, CSV, HTML). Dovresti anche considerare l'utilizzo di ServiceStack che ho creato esattamente per questo scopo in cui ogni servizio che sviluppi è automaticamente disponibile su endpoint SOAP e REST immediatamente senza alcuna configurazione richiesta.

L' esempio Hello World mostra come creare un servizio semplice con solo (nessuna configurazione richiesta):

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

Non è richiesta altra configurazione e questo servizio è immediatamente disponibile con REST in:

Viene inoltre integrato con un output HTML intuitivo (quando viene chiamato con un client HTTP che ha Accept: text / html, ad esempio un browser) in modo da poter visualizzare meglio l'output dei tuoi servizi.

Gestire diversi verbi REST è altrettanto banale, ecco un'app CRUD completa di servizi REST in 1 pagina di C # (meno di quanto sarebbe necessario per configurare WCF;):


7

MSDN sembra avere un articolo per questo ora:

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

Intro:

Per impostazione predefinita, Windows Communication Foundation (WCF) rende gli endpoint disponibili solo per i client SOAP. In Procedura: creare un servizio HTTP Web WCF di base, un endpoint è reso disponibile per i client non SOAP. In alcuni casi è possibile rendere disponibile lo stesso contratto in entrambi i modi, come endpoint Web e endpoint SOAP. Questo argomento mostra un esempio di come eseguire questa operazione.


3

Dobbiamo definire la configurazione del comportamento sull'endpoint REST

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

e anche a un servizio

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

Dopo i comportamenti, il passo successivo sono i vincoli. Ad esempio basicHttpBinding su endpoint SOAP e webHttpBinding su REST .

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

Infine, dobbiamo definire l'endpoint 2 nella definizione del servizio. Attenzione per l'indirizzo = "" dell'endpoint, dove al servizio REST non è necessario nulla.

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

In Interfaccia del servizio definiamo l'operazione con i suoi attributi.

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

Unendo tutte le parti, questa sarà la nostra definizione di system.serviceModel WCF.

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

Per testare entrambi gli endpoint, possiamo usare WCFClient per SOAP e PostMan per REST .


Funziona bene come previsto
Shiv

0

Questo è quello che ho fatto per farlo funzionare. Assicurati di mettere
webHttp automaticFormatSelectionEnabled = "true" all'interno del comportamento dell'endpoint.

[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...";
    }
}

Modello di servizio interno

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

Comportamento di EndPoint

  <endpointBehaviors>
    <behavior name="jsonBehavior">
      <webHttp automaticFormatSelectionEnabled="true"  />
      <!-- use JSON serialization -->
    </behavior>
  </endpointBehaviors>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.