Pontos de extremidade REST / SOAP para um serviço WCF


425

Eu tenho um serviço WCF e quero expô-lo como um serviço RESTfull e como um serviço SOAP. Alguém já fez algo assim antes?


boa pergunta e ótimas respostas.
Chandra rv

Respostas:


584

Você pode expor o serviço em dois pontos de extremidade diferentes. o SOAP pode usar a ligação que suporta SOAP, por exemplo, basicHttpBinding, o RESTful pode usar o webHttpBinding. Presumo que seu serviço REST esteja em JSON; nesse caso, você precisa configurar os dois pontos de extremidade com a seguinte configuração de comportamento

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

Um exemplo de configuração de terminal em seu cenário é

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

portanto, o serviço estará disponível em

Aplique [WebGet] ao contrato de operação para torná-lo RESTful. por exemplo

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

Observe que, se o serviço REST não estiver em JSON, os parâmetros das operações não poderão conter um tipo complexo.

Responder à postagem de SOAP e RESTful POX (XML)

Para XML antigo simples como formato de retorno, este é um exemplo que funcionaria tanto para SOAP quanto XML.

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

Comportamento do POX para XML antigo simples REST

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

Pontos finais

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

O serviço estará disponível em

Solicitação REST, experimente no navegador,

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

SOAP solicita a configuração do terminal do cliente para o serviço SOAP após incluir a referência de serviço,

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

em c #

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

Outra maneira de fazer isso é expor dois contratos de serviço diferentes e cada um com configuração específica. Isso pode gerar algumas duplicatas no nível do código; no entanto, no final do dia, você deseja fazê-lo funcionar.


11
Como isso parece quando eu tenho .svc hospedado no IIS em algum diretório virtual como someserver / myvirtualdir / service.svc ? Como devo acessá-lo?
Sunny Milenov

Gostaria de dar um passo adiante e adicionar uma ligação ao HTTPS para o endereço JSON. Como faço isso? stackoverflow.com/questions/18213472/…
Steve

Ele está dizendo que meu contrato IEvents é inválido quando tento fazer referência à minha interface de serviço: <service name = "Events"> <endereço do ponto de extremidade = "json" binding = "webHttpBinding" behaviorConfiguration = "jsonBehavior" contract = "IEvents" />. Meus IEvents possuem um atributo [ServiceContract] na interface, portanto, não sei por quê. </service>
PositiveGuy

Posso obter localhost: 44652 / MyResource / json para funcionar, mas não consigo obter um ID para trabalhar localhost: 44652 / MyResource / 98 / json . Tentei adicionar um UriTemplate de "/ {id}", também tentei "events / {id}, mas não o encontra quando tento acessar o serviço. Apenas o primeiro funciona, não sei como obtê-lo. ao trabalho.
PositiveGuy

2
Como ele pode funcionar sem nenhum arquivo físico lá? Eu só parecem ter erros 404, deve estar faltando alguma coisa
RoboJ1M

39

Este post já tem uma resposta muito boa por "wiki comunitário" e eu também recomendo a olhar para Web Blog do Rick Strahl, há muitas mensagens boas sobre WCF de lazer como este .

Eu usei os dois para obter esse tipo de serviço MyService ... Então eu posso usar a interface REST do jQuery ou SOAP do Java.

Isto é do meu 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 esta é a minha classe de serviço (.svc-codebehind, sem necessidade de interfaces):

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

Na verdade, eu uso apenas Json ou Xml, mas os dois estão aqui para fins de demonstração. Essas são solicitações GET para obter dados. Para inserir dados, eu usaria o método com atributos:

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

Estou curioso para saber quais benefícios você acredita que obterá adicionando esses atributos WebGet e WebInvoke.
Darrel Miller

2
Você pode fazer solicitações pelo navegador: localhost / MyService.svc / MyXmlResource / test E diga explicitamente o formato Json ou Xml. Se você deseja que os mesmos métodos respondam aos dois, aqui está um link: blogs.msdn.com/dotnetinterop/archive/2008/11/04/…
Tuomas Hietanen

Isso é para fins de teste. Apenas para ver se seus terminais estão funcionando. Você já viu o SoapUI? soapui.org
Darrel Miller

@TuomasHietanen - Eu não recebo resposta do tipo JSON usando o comportamento webHttp, mas usando o enableWebScript eu recebo a resposta do tipo JSON. Coloquei ResponseFormat como WebMessageFormat.Json. Por outro lado, não posso usar o URItemplate se usar o comportamento enableWebScript. Alguma ideia?
smile.al.d.way

1
@ CoffeeAddict - Por que você deve usar a interface? Só para ter interface? Você nunca reutilizará essa interface. Isto é mais simples.
Tuomas Hietanen

25

Se você deseja desenvolver apenas um único serviço da Web e hospedá-lo em muitos pontos de extremidade diferentes (por exemplo, SOAP + REST, com saídas XML, JSON, CSV, HTML). Você também deve considerar usar o ServiceStack que eu criei exatamente para esse fim, onde todos os serviços que você desenvolve estão automaticamente disponíveis nos pontos de extremidade SOAP e REST prontos para uso, sem a necessidade de nenhuma configuração.

O exemplo Hello World mostra como criar um serviço simples com apenas (nenhuma configuração necessária):

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

Nenhuma outra configuração é necessária e este serviço está disponível imediatamente com o REST em:

Ele também vem embutido com uma saída HTML amigável (quando chamada com um cliente HTTP que possui Accept: text / html, por exemplo, um navegador) para que você possa visualizar melhor a saída de seus serviços.

A manipulação de verbos REST diferentes também é tão trivial. Aqui está um aplicativo CRUD de serviço REST completo em 1 página de C # (menos do que seria necessário para configurar o WCF;):


7

O MSDN parece ter um artigo para isso agora:

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

Introdução:

Por padrão, o Windows Communication Foundation (WCF) disponibiliza pontos de extremidade apenas para clientes SOAP. Em Como: Criar um Serviço HTTP da Web Básico do WCF, um ponto de extremidade é disponibilizado para clientes não SOAP. Pode haver momentos em que você queira disponibilizar o mesmo contrato nos dois sentidos, como um terminal da Web e SOAP. Este tópico mostra um exemplo de como fazer isso.


3

Devemos definir a configuração de comportamento para o terminal REST

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

e também para um serviço

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

Após os comportamentos, o próximo passo é as ligações. Por exemplo, basicHttpBinding para o terminal SOAP e webHttpBinding para REST .

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

Finalmente, devemos definir o ponto final 2 na definição de serviço. Atenção para o endereço = "" do nó de extremidade, onde REST o serviço não é necessário nada.

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

Na interface do serviço, definimos a operação com seus atributos.

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

Juntando-se a todas as partes, esta será a nossa definição system.serviceModel do 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>

Para testar os dois pontos de extremidade, podemos usar WCFClient para SOAP e PostMan para REST .


Trabalhando bem como esperado
Shiv

0

Isto é o que eu fiz para fazê-lo funcionar. Certifique-se de colocar
webHttp automaticFormatSelectionEnabled = "true" dentro do comportamento do terminal.

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

Modelo de serviço 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 do EndPoint

  <endpointBehaviors>
    <behavior name="jsonBehavior">
      <webHttp automaticFormatSelectionEnabled="true"  />
      <!-- use JSON serialization -->
    </behavior>
  </endpointBehaviors>
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.