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.