427

I have a WCF service and I want to expose it as both a RESTfull service and as a SOAP service. Anyone has done something like this before?

John Saunders
  • 157,405
  • 24
  • 229
  • 388
Wessam Zeidan
  • 4,421
  • 3
  • 16
  • 7

6 Answers6

586

You can expose the service in two different endpoints. the SOAP one can use the binding that support SOAP e.g. basicHttpBinding, the RESTful one can use the webHttpBinding. I assume your REST service will be in JSON, in that case, you need to configure the two endpoints with the following behaviour configuration

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

An example of endpoint configuration in your scenario is

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

so, the service will be available at

Apply [WebGet] to the operation contract to make it RESTful. e.g.

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

Note, if the REST service is not in JSON, parameters of the operations can not contain complex type.

Reply to the post for SOAP and RESTful POX(XML)

For plain old XML as return format, this is an example that would work both for SOAP and XML.

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

POX behavior for REST Plain Old XML

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

Endpoints

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

Service will be available at

REST request try it in browser,

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

SOAP request client endpoint configuration for SOAP service after adding the service reference,

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

Another way of doing it is to expose two different service contract and each one with specific configuration. This may generate some duplicates at code level, however at the end of the day, you want to make it working.

Jay
  • 4,042
  • 3
  • 23
  • 37
Ray Lu
  • 24,557
  • 12
  • 57
  • 59
  • 11
    How does this look like when I have .svc hosted in IIS in some virtual directory like http://someserver/myvirtualdir/service.svc? How should I access it? – Sunny Milenov Jul 14 '11 at 12:43
  • I'd like to take this one step further and add a binding to HTTPS for the JSON address. How do I do that? http://stackoverflow.com/questions/18213472/how-do-i-bind-wcf-json-to-https-with-multiple-bindings – Steve Aug 13 '13 at 20:05
  • It's saying my contract IEvents is invalid when I try to reference my Service Interface: . My IEvents has a [ServiceContract] attribute on the interface so not sure why. – PositiveGuy Oct 13 '13 at 03:54
  • I can get http://localhost:44652/MyResource/json to work but I can't get an id to work http://localhost:44652/MyResource/98/json. I've tried adding a UriTemplate of "/{id}", also tried "events/{id} but it doesn't find it when I try to hit the service. Only the first works, not sure how to get the latter to work. – PositiveGuy Oct 15 '13 at 16:13
  • 2
    How can it work with no physical file there? I just seem to get 404 errors, must be missing something – RoboJ1M May 08 '14 at 14:58
  • How do you deal with a operation which has complex type input parameter and that is to be like that for SOAP calls and another method/operation with rest. Operation 'MethodName' in contract 'IInterface' has a query variable named 'ParamOne' of type 'ComplexType', but type 'ComplexType' is not convertible by 'QueryStringConverter'. Variables for UriTemplate query values must have types that can be converted by 'QueryStringConverter' – HaBo Dec 01 '14 at 16:02
  • What if I have 2 methods and want to expose both with soap and ONLY one with rest? – alexbrina Nov 08 '17 at 13:27
  • https://andrearegoli.wordpress.com/2014/06/05/wcf-rest-configure-endopoint-to-return-soap-xml-and-json/ ***WCF REST*** returns `SOAP, XML, JSON` – PreguntonCojoneroCabrón Feb 21 '18 at 22:45
39

This post has already a very good answer by "Community wiki" and I also recommend to look at Rick Strahl's Web Blog, there are many good posts about WCF Rest like this.

I used both to get this kind of MyService-service... Then I can use the REST-interface from jQuery or SOAP from Java.

This is from my 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>

And this is my service-class (.svc-codebehind, no interfaces required):

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

Actually I use only Json or Xml but those both are here for a demo purpose. Those are GET-requests to get data. To insert data I would use method with attributes:

[OperationContract(Name = "MyResourceSave")]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, UriTemplate = "MyJsonResource")]
public string MyResourceSave(string thing){
    //...
Tuomas Hietanen
  • 3,945
  • 1
  • 31
  • 41
  • I am curious to know what benefits you believe you will get by adding these WebGet and WebInvoke attributes. – Darrel Miller Feb 19 '10 at 12:14
  • 2
    You can make requests by browser: http://localhost/MyService.svc/MyXmlResource/test And explicitly say format Json or Xml. If you want same methods to respond both, here is a link: http://blogs.msdn.com/dotnetinterop/archive/2008/11/04/rest-in-wcf-varying-response-content-type-based-on-http-request-headers.aspx – Tuomas Hietanen Feb 19 '10 at 12:27
  • This is for testing purposes. Just to see if your endpoints are working. Have you looked at SoapUI? http://www.soapui.org/ – Darrel Miller Feb 19 '10 at 21:24
  • @TuomasHietanen - I do not get JSON type response by using webHttp behavior however using enableWebScript i do get JSON type response. I did put ResponseFormat as WebMessageFormat.Json. On the other hand I cannot use URItemplate if I use enableWebScript behavior. Any ideas? – smile.al.d.way Oct 20 '11 at 03:17
  • why are you not using interfaces with your service class? weird. – PositiveGuy Oct 13 '13 at 04:00
  • 1
    @CoffeeAddict - Why should you use inteface? Just to have interface? You won't reuse this interface ever. This is simpler. – Tuomas Hietanen Nov 25 '13 at 10:24
25

If you only want to develop a single web service and have it hosted on many different endpoints (i.e. SOAP + REST, with XML, JSON, CSV, HTML outputes). You should also consider using ServiceStack which I've built for exactly this purpose where every service you develop is automatically available on on both SOAP and REST endpoints out-of-the-box without any configuration required.

The Hello World example shows how to create a simple with service with just (no config required):

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 other configuration is required, and this service is immediately available with REST in:

It also comes in-built with a friendly HTML output (when called with a HTTP client that has Accept:text/html e.g a browser) so you're able to better visualize the output of your services.

Handling different REST verbs are also as trivial, here's a complete REST-service CRUD app in 1 page of C# (less than it would take to configure WCF ;):

mythz
  • 134,801
  • 25
  • 234
  • 373
7

MSDN seems to have an article for this now:

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

Intro:

By default, Windows Communication Foundation (WCF) makes endpoints available only to SOAP clients. In How to: Create a Basic WCF Web HTTP Service, an endpoint is made available to non-SOAP clients. There may be times when you want to make the same contract available both ways, as a Web endpoint and as a SOAP endpoint. This topic shows an example of how to do this.

FMFF
  • 1,505
  • 4
  • 28
  • 55
3

We must define the behavior configuration to REST endpoint

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

and also to a service

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

After the behaviors, next step is the bindings. For example basicHttpBinding to SOAP endpoint and webHttpBinding to REST.

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

Finally we must define the 2 endpoint in the service definition. Attention for the address="" of endpoint, where to REST service is not necessary nothing.

<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 Interface of the service we define the operation with its attributes.

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

Joining all parties, this will be our WCF system.serviceModel definition.

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

To test the both endpoint, we can use WCFClient to SOAP and PostMan to REST.

0

This is what i did to make it work. Make sure you put
webHttp automaticFormatSelectionEnabled="true" inside endpoint behaviour.

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

Inside service model

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

EndPoint Behaviour

  <endpointBehaviors>
    <behavior name="jsonBehavior">
      <webHttp automaticFormatSelectionEnabled="true"  />
      <!-- use JSON serialization -->
    </behavior>
  </endpointBehaviors>
Nayas Subramanian
  • 1,901
  • 16
  • 24