Puntos finales REST / SOAP para un servicio WCF

425

Tengo un servicio WCF y quiero exponerlo como un servicio RESTfull y como un servicio SOAP. ¿Alguien ha hecho algo como esto antes?

Wessam Zeidan
fuente
Buena pregunta y excelentes respuestas.
chandra rv

Respuestas:

584

Puede exponer el servicio en dos puntos finales diferentes. el SOAP puede usar el enlace que admite SOAP, por ejemplo, basicHttpBinding, el RESTful puede usar el webHttpBinding. Supongo que su servicio REST estará en JSON, en ese caso, debe configurar los dos puntos finales con la siguiente configuración de comportamiento

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

Un ejemplo de configuración de punto final en su escenario es

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

entonces, el servicio estará disponible en

Aplique [WebGet] al contrato de operación para que sea RESTful. p.ej

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

Tenga en cuenta que si el servicio REST no está en JSON, los parámetros de las operaciones no pueden contener tipos complejos.

Responder a la publicación de SOAP y RESTful POX (XML)

Para XML antiguo simple como formato de retorno, este es un ejemplo que funcionaría tanto para SOAP como para XML.

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

Comportamiento de POX para REST Plain Old XML

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

Puntos finales

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

El servicio estará disponible en

Solicitud REST pruébalo en el navegador,

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

Configuración del punto final del cliente de solicitud SOAP para el servicio SOAP después de agregar la referencia del servicio,

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

C ª#

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

Otra forma de hacerlo es exponer dos contratos de servicio diferentes y cada uno con una configuración específica. Esto puede generar algunos duplicados a nivel de código, sin embargo, al final del día, desea que funcione.

codemeit
fuente
11
¿Cómo se ve esto cuando tengo .svc alojado en IIS en algún directorio virtual como someserver / myvirtualdir / service.svc ? ¿Cómo debo acceder?
Sunny Milenov
Me gustaría ir un paso más allá y agregar un enlace a HTTPS para la dirección JSON. ¿Cómo puedo hacer eso? stackoverflow.com/questions/18213472/…
Steve
Dice que mi contrato IEvents no es válido cuando intento hacer referencia a mi interfaz de servicio: <service name = "Events"> <endpoint address = "json" binding = "webHttpBinding" behaviourConfiguration = "jsonBehavior" contract = "IEvents" />. My IEvents tiene un atributo [ServiceContract] en la interfaz, así que no estoy seguro de por qué. </service>
PositiveGuy
Puedo hacer que localhost: 44652 / MyResource / json funcione, pero no puedo obtener una identificación para que funcione localhost: 44652 / MyResource / 98 / json . Intenté agregar un UriTemplate de "/ {id}", también probé "events / {id} pero no lo encuentro cuando intento acceder al servicio. Solo el primero funciona, no estoy seguro de cómo obtener el último para trabajar.
PositiveGuy
2
¿Cómo puede funcionar sin archivo físico allí? Parece que recibo errores 404, me falta algo
RoboJ1M
39

Esta publicación ya tiene una muy buena respuesta de "Wiki de la comunidad" y también recomiendo mirar el blog web de Rick Strahl, hay muchas publicaciones buenas sobre WCF Rest como esta .

Usé ambos para obtener este tipo de servicio MyService ... Entonces puedo usar la interfaz REST de jQuery o SOAP de Java.

Esto es de mi web.

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

Y esta es mi clase de servicio (.svc-codebehind, no se requieren 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;
    }
}

En realidad, solo uso Json o Xml, pero esos dos están aquí con fines de demostración. Esas son solicitudes GET para obtener datos. Para insertar datos, usaría el método con atributos:

[OperationContract(Name = "MyResourceSave")]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, UriTemplate = "MyJsonResource")]
public string MyResourceSave(string thing){
    //...
Tuomas Hietanen
fuente
Tengo curiosidad por saber qué beneficios cree que obtendrá al agregar estos atributos WebGet y WebInvoke.
Darrel Miller
2
Puede realizar solicitudes por navegador: localhost / MyService.svc / MyXmlResource / test y decir explícitamente el formato Json o Xml. Si desea los mismos métodos para responder a ambos, aquí hay un enlace: blogs.msdn.com/dotnetinterop/archive/2008/11/04/…
Tuomas Hietanen
Esto es para fines de prueba. Solo para ver si sus puntos finales funcionan. ¿Has mirado en SoapUI? soapui.org
Darrel Miller
@TuomasHietanen: no obtengo respuesta de tipo JSON mediante el uso del comportamiento webHttp; sin embargo, usando enableWebScript sí obtengo respuesta de tipo JSON. Puse ResponseFormat como WebMessageFormat.Json. Por otro lado, no puedo usar URItemplate si uso el comportamiento enableWebScript. ¿Algunas ideas?
smile.al.d.way
1
@CoffeeAddict - ¿Por qué deberías usar la interfaz? ¿Solo para tener interfaz? No volverás a usar esta interfaz nunca. Esto es mas simple.
Tuomas Hietanen
25

Si solo desea desarrollar un único servicio web y alojarlo en muchos puntos finales diferentes (es decir, SOAP + REST, con salidas XML, JSON, CSV, HTML). También debe considerar usar ServiceStack, que he creado exactamente para este propósito, donde cada servicio que desarrolla está disponible automáticamente en los puntos finales SOAP y REST de forma inmediata sin ninguna configuración requerida.

El ejemplo de Hello World muestra cómo crear un servicio simple con solo (no se requiere configuración):

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

No se requiere ninguna otra configuración, y este servicio está disponible de inmediato con REST en:

También viene incorporado con una salida HTML amigable (cuando se llama con un cliente HTTP que tiene Aceptar: texto / html, por ejemplo, un navegador) para que pueda visualizar mejor la salida de sus servicios.

El manejo de diferentes verbos REST también es trivial, aquí hay una aplicación CRUD de servicio REST completa en 1 página de C # (menos de lo que se necesitaría para configurar WCF;):

mythz
fuente
7

MSDN parece tener un artículo para esto ahora:

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

Introducción:

De manera predeterminada, Windows Communication Foundation (WCF) hace que los puntos finales estén disponibles solo para clientes SOAP. En Cómo: Crear un servicio HTTP web básico de WCF, un punto final está disponible para clientes que no son SOAP. Puede haber ocasiones en que desee que el mismo contrato esté disponible en ambos sentidos, como punto final web y como punto final SOAP. Este tema muestra un ejemplo de cómo hacer esto.

FMFF
fuente
3

Debemos definir la configuración del comportamiento para el punto final REST

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

y también a un servicio

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

Después de los comportamientos, el siguiente paso son los enlaces. Por ejemplo, basicHttpBinding to SOAP endpoint y webHttpBinding to REST .

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

Finalmente, debemos definir el punto final 2 en la definición del servicio. Atención a la dirección = "" del punto final, donde REST servicio no es necesario 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>

En la interfaz del servicio definimos la operación con sus 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);
    }
}

Uniéndose a todas las partes, esta será nuestra definición de 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>

Para probar ambos puntos finales, podemos usar WCFClient para SOAP y PostMan para REST .

Jailson Evora
fuente
Funciona bien como se esperaba
Shiv
0

Esto es lo que hice para que funcione. Asegúrese de poner
webHttp automaticFormatSelectionEnabled = "true" dentro del comportamiento del punto 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...";
    }
}

Modelo de servicio 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>

Comportamiento de EndPoint

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