Punkty końcowe REST / SOAP dla usługi WCF

425

Mam usługę WCF i chcę ją udostępnić zarówno jako usługę RESTfull, jak i jako usługę SOAP. Czy ktoś zrobił coś takiego wcześniej?

Wessam Zeidan
źródło
dobre pytanie i świetne odpowiedzi.
chandra rv

Odpowiedzi:

584

Możesz udostępnić usługę w dwóch różnych punktach końcowych. SOAP można użyć powiązania, które obsługuje SOAP, np. basicHttpBinding, RESTful można użyć webHttpBinding. Zakładam, że twoja usługa REST będzie w JSON, w takim przypadku musisz skonfigurować dwa punkty końcowe za pomocą następującej konfiguracji zachowania

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

Przykład konfiguracji punktu końcowego w twoim scenariuszu to

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

usługa będzie dostępna pod adresem

Zastosuj [WebGet] do umowy operacyjnej, aby ją zrestartować. na przykład

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

Uwaga: jeśli usługa REST nie znajduje się w JSON, parametry operacji nie mogą zawierać typu złożonego.

Odpowiedz na post dla SOAP i RESTful POX (XML)

Dla zwykłego starego XML jako formatu zwracanego jest to przykład, który działałby zarówno dla SOAP, jak i XML.

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

Zachowanie POX dla REST Plain Old XML

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

Punkty końcowe

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

Usługa będzie dostępna pod adresem

Żądanie REST spróbuj w przeglądarce,

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

Konfiguracja punktu końcowego klienta żądania SOAP dla usługi SOAP po dodaniu odwołania do usługi,

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

w C #

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

Innym sposobem jest ujawnienie dwóch różnych umów serwisowych i każdego z określoną konfiguracją. Może to generować niektóre duplikaty na poziomie kodu, jednak pod koniec dnia chcesz, aby działało.

codemeit
źródło
11
Jak to wygląda, gdy mam .svc hostowany w IIS w jakimś katalogu wirtualnym, takim jak someserver / myvirtualdir / service.svc ? Jak mam uzyskać do niego dostęp?
Sunny Milenov
Chciałbym pójść o krok dalej i dodać powiązanie do HTTPS dla adresu JSON. Jak mogę to zrobić? stackoverflow.com/questions/18213472/...
Steve,
Mówi, że moja umowa IEvents jest nieważna, gdy próbuję odwołać się do mojego interfejsu usługi: <nazwa usługi = „Zdarzenia”> <adres punktu końcowego = „json” powiązanie = „webHttpBinding” behawioracja = „jsonBehavior” umowa = „IEvent” />. Moje IEvents ma w interfejsie atrybut [ServiceContract], więc nie jestem pewien, dlaczego. </service>
PositiveGuy,
Mogę uzyskać localhost: 44652 / MyResource / json do pracy, ale nie mogę uzyskać identyfikatora do pracy localhost: 44652 / MyResource / 98 / json . Próbowałem dodać UriTemplate z „/ {id}”, próbowałem również „events / {id}, ale nie mogę go znaleźć, gdy próbuję uruchomić usługę. Tylko pierwsza działa, nie wiem, jak ją zdobyć do pracy
PositiveGuy
2
Jak może działać bez pliku fizycznego? Wydaje mi się, że dostaję błędy 404, czegoś mi brakuje
RoboJ1M
39

Ten post ma już bardzo dobrą odpowiedź przez „Community wiki”, a także polecam zajrzeć na blog internetowy Ricka Strahla, istnieje wiele dobrych postów na temat WCF Rest w ten sposób .

Użyłem obu, aby uzyskać tego rodzaju usługę MyService ... Następnie mogę użyć interfejsu REST z jQuery lub SOAP z Java.

To jest z mojego 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>

A to moja klasa usług (.svc-codebehind, nie wymaga interfejsów):

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

Właściwie używam tylko Jsona lub Xml, ale oba są tutaj w celach demonstracyjnych. Są to żądania GET, aby uzyskać dane. Aby wstawić dane, użyłbym metody z atrybutami:

[OperationContract(Name = "MyResourceSave")]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, UriTemplate = "MyJsonResource")]
public string MyResourceSave(string thing){
    //...
Tuomas Hietanen
źródło
Jestem ciekawy, jakie korzyści uzyskasz, dodając te atrybuty WebGet i WebInvoke.
Darrel Miller
2
Możesz wysyłać żądania za pomocą przeglądarki: localhost / MyService.svc / MyXmlResource / test I jawnie powiedz format Json lub Xml. Jeśli chcesz, aby te same metody odpowiedziały na oba, oto link: blogs.msdn.com/dotnetinterop/archive/2008/11/04/...
Tuomas Hietanen
To jest do celów testowych. Tylko po to, aby sprawdzić, czy Twoje punkty końcowe działają. Czy spojrzałeś na SoapUI? soapui.org
Darrel Miller
@TuomasHietanen - Nie otrzymuję odpowiedzi typu JSON za pomocą zachowania webHttp, jednak za pomocą enableWebScript otrzymuję odpowiedź typu JSON. Umieściłem ResponseFormat jako WebMessageFormat.Json. Z drugiej strony nie mogę użyć URItemplate, jeśli użyję zachowania enableWebScript. Jakieś pomysły?
smile.al.d.way 20.10.11
1
@CoffeeAddict - Dlaczego warto korzystać z interfejsu? Po prostu mieć interfejs? Nigdy więcej nie użyjesz tego interfejsu. To jest prostsze.
Tuomas Hietanen,
25

Jeśli chcesz opracować tylko jedną usługę internetową i hostować ją w wielu różnych punktach końcowych (tj. SOAP + REST, z wyjściami XML, JSON, CSV, HTML). Powinieneś także rozważyć użycie ServiceStack, który zbudowałem właśnie do tego celu, w którym każda usługa, którą opracowujesz, jest automatycznie dostępna w punktach końcowych SOAP i REST natychmiast po wyjęciu z pudełka, bez wymaganej konfiguracji.

Przykład Hello World pokazuje, jak stworzyć prosty z usługą za pomocą just (nie wymaga konfiguracji):

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

Żadna inna konfiguracja nie jest wymagana, a ta usługa jest natychmiast dostępna z REST w:

Jest również wbudowany w przyjazny wynik HTML (gdy jest wywoływany z klientem HTTP, który ma opcję Akceptuj: tekst / HTML np. Przeglądarka), dzięki czemu możesz lepiej wizualizować wyniki swoich usług.

Obsługa różnych czasowników REST jest równie trywialna, oto pełna aplikacja CRUD z usługą REST na 1 stronie C # (mniej niż zajęłoby to skonfigurowanie WCF;):

mit
źródło
7

Wydaje się, że MSDN ma teraz na ten temat artykuł:

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

Wprowadzenie:

Domyślnie Windows Communication Foundation (WCF) udostępnia punkty końcowe tylko klientom SOAP. W temacie: Tworzenie podstawowej usługi HTTP HTTP WCF punkt końcowy został udostępniony klientom innym niż SOAP. Może się zdarzyć, że chcesz udostępnić tę samą umowę w obie strony, jako punkt końcowy sieci Web i punkt końcowy SOAP. W tym temacie pokazano przykład tego, jak to zrobić.

FMFF
źródło
3

Musimy zdefiniować konfigurację zachowania do punktu końcowego REST

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

a także do usługi

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

Po zachowaniach następnym krokiem są powiązania. Na przykład basicHttpBinding do punktu końcowego SOAP i webHttpBinding do REST .

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

Wreszcie musimy zdefiniować 2 punkt końcowy w definicji usługi. Uwaga na adres = "" punktu końcowego, gdzie do usługi REST nie jest konieczne nic.

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

W interfejsie usługi definiujemy operację za pomocą jej atrybutów.

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

Dołączając do wszystkich stron, będzie to nasza definicja systemu WCF. 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>

Aby przetestować oba punkty końcowe, możemy użyć WCFClient do SOAP i PostMan do REST .

Jailson Evora
źródło
Działa dobrze, zgodnie z oczekiwaniami
Shiv
0

Tak zrobiłem, aby działało. Upewnij się, że umieściłeś
webHttp automaticFormatSelectionEnabled = "true" w zachowaniu punktu końcowego.

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

Wewnątrz model serwisowy

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

Zachowanie EndPoint

  <endpointBehaviors>
    <behavior name="jsonBehavior">
      <webHttp automaticFormatSelectionEnabled="true"  />
      <!-- use JSON serialization -->
    </behavior>
  </endpointBehaviors>
Nayas Subramanian
źródło