Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combined SOAP/JSON/XML in WCF, using UriTemplate

I'm trying to build a generic web service interface using WCF, to allow 3rd party developers to hook into our software. After much struggling and reading (this question helped a lot), I finally got SOAP, JSON and XML (POX) working together.

To simplify, here's my code (to make this example simple, I'm not using interfaces -- I did try this both ways):

<ServiceContract()> _
Public Class TestService
    Public Sub New()
    End Sub

    <OperationContract()> _
    <WebGet()> _
    Public Function GetDate() As DateTime
        Return Now
    End Function


    '<WebGet(UriTemplate:="getdateoffset/{numDays}")> _
    <OperationContract()> _
    Public Function GetDateOffset(ByVal numDays As Integer) As DateTime
        Return Now.AddDays(numDays)
    End Function

End Class

and the web.config code:

<services>
  <service name="TestService" 
           behaviorConfiguration="TestServiceBehavior">
    <endpoint address="soap" binding="basicHttpBinding" contract="TestService"/>
    <endpoint address="json" binding="webHttpBinding" behaviorConfiguration="jsonBehavior" contract="TestService"/>
    <endpoint address="xml" binding="webHttpBinding" behaviorConfiguration="poxBehavior" contract="TestService"/>
    <endpoint address="mex" contract="IMetadataExchange" binding="mexHttpBinding" />
  </service>
</services>
<behaviors>
  <endpointBehaviors>
    <behavior name="jsonBehavior">
      <enableWebScript/>
    </behavior>
    <behavior name="poxBehavior">
      <webHttp />
    </behavior>
  </endpointBehaviors>
  <serviceBehaviors>
    <behavior name="TestServiceBehavior">
      <serviceMetadata httpGetEnabled="true"/>
      <serviceDebug includeExceptionDetailInFaults="false"/>
    </behavior>
  </serviceBehaviors>
</behaviors>

This actually works -- I'm able to go to TestService.svc/xml/GetDate for xml, TestService.svc/json/GetDate for json, and point a SOAP client at TestService.svc?wsdl and have the SOAP queries work.

The part I'd like to fix is the queries. I have to use TestService.svc/xml/GetDateOffset?numDays=4 instead of TestService.svc/xml/GetDateOffset/4. If I specify the UriTemplate, I get the error:

Endpoints using 'UriTemplate' cannot be used with 'System.ServiceModel.Description.WebScriptEnablingBehavior'.

But of course without using <enableWebScript/>, JSON doesn't work.

The only other thing I've seen that I think will work is making 3 different services (.svc files), that all implement an interface that specifies the contract, but in the classes specify different WebGet/WebInvoke attributes on each class. This seems like a lot of extra work, that frankly, I don't see why the framework doesn't handle for me. The implementation of the classes would all be the same, except for the attributes, which means over time it would be easy for bugs/changes to get fixed/done in one implementation but not the others, leading to inconsistent behaviour when using the JSON vs SOAP implementation for example.

Am I doing something wrong here? Am I taking a totally wrong approach and misusing WCF? Is there a better way to do this?

With my experience doing web stuff, I think it should be possible for some kind of framework to handle this ... I even have an idea in my head of how to build it. It just seems like WCF is supposed to be doing this, and I don't really want to reinvent the wheel.

like image 842
gregmac Avatar asked Mar 18 '10 16:03

gregmac


2 Answers

Actually <enableWebScript /> is not required for "pure" JSON support. The WebScriptEnablingBehavior is only required if you want to support ASP.NET AJAX. More often than not, if you're trying to work with standard script libraries, you don't want to enable this support for your services.

Instead what you want to do for your JSON endpoint is just use the WebHttpBehavior and set the DefaultOutgoingResponseFormat="JSON". The problem is, in .NET 3.5, you cannot control this setting through config because WebHttpElement does not expose these properties for configuration. To work around this in 3.5 have supplied an implementation for what I call the EnhancedWebHttpElement here in this answer to another StackOverflow question.

Luckily Microsoft realized this shortcoming and enabled configuration of all the WebHttpBehavior settings via the WebHttpElement in 4.0.

like image 135
Drew Marsh Avatar answered Oct 17 '22 06:10

Drew Marsh


Drew's answer is spot on, but I think the question still stands. Is there a sane way of having JSON for AJAX () and the blessings of UriTemplate?

I think it is worth mentioning that JSON returned with is different than JSON generated with [WebGet(ResponseFormat=WebMessageFormat.Json)]. The former is wrapped around the the MS AJAX 'd' element e.g. {"d":[{...}]}.

like image 36
tymtam Avatar answered Oct 17 '22 07:10

tymtam