« Ctrl-Q + IE7 = Cool Tiles | Main | Thank you »

2007.05.08

WCF for the Web - a Smackdown

I thoroughly enjoyed the opportunity to learn about the new model, and I am very excited to see the capabilities baked into WCF. The web is a great platform for integration, and so having first class support in WCF is critical for WCF to be useful in truly large-scale context. When I was speaking with Steve Maine, I mentioned that my base level for comparison for the programming model is starting from a raw socket. How far off of the raw socket experience am I, and what am I gaining from the extra layers?

OK. I might not actually go all the way down to a raw socket, but I would go at least to a CGI/Servlet/ISAPI Filter/IHttpHandler/HttpListener level. That is, assuming that I don't have to reinvent listeners or perhaps the extensible web server as a concept, how different and flexible is the model? I think that, in the case of Microsoft.ServiceModel.Web, the answer is going to be "A good bit."

But.

So.

My first naive attempt at a SmackDown failed relatively miserably. I thought I would pair up Harry "I Http" Handler versus Spidey "the web" .svc.

So, I added an IHttpHandler:

<%@ WebHandler Language="C#" Class="Hello" %>

 

using System;

using System.Web;

 

public class Hello : IHttpHandler {

 

    public void ProcessRequest (HttpContext context) {

        context.Response.ContentType = "text/xml";

        context.Response.Write("<Say>Hello, Cruel World.</Say>");

    }

 

    public bool IsReusable { get { return true; } }

}

(Note: I am using Windows Live Writer, and it doesn't allow me to paste rich text content. Argh. Copy Source As HTML to the rescue.)

I do a GET against the Handler, and viola!

Now granted, I am not even checking to see if the method is GET. I might want to add that in, eh? But for now, it works.

I added a WCF service to the web project (I skipped defining a service contract interface at this point to keep it simple. In actuality, I did it both with and without):

<%@ ServiceHost Language=C# Debug="true" Service="Improving.Samples.WCF.Web.Hello" %>

 

using System.ServiceModel;

using System.ServiceModel.Web;

 

namespace Improving.Samples.WCF.Web

{

    [ServiceContract()]

    public class Hello

    {

        [OperationContract]

        [WebGet()]

        // Not very, um, resourcey, but...

        public string GetMessage()

        {

            return "Hello, Cruel World";

        }

    }

}

wired up the service in the web.config, changed the binding from wsHttpBinding to webHttpBinding:

  <system.serviceModel>

    <services>

      <service name="Improving.Samples.WCF.Web.Hello">

        <endpoint contract="Improving.Samples.WCF.Web.Hello" binding="webHttpBinding" />

      </service>

    </services>

  </system.serviceModel>

Do a GET in the browser, and voila!

 

 

 

 

 

 

 

 

 

 

 

 

OK. OK. Minor set back. Let's try to add the binding in:

  <system.serviceModel>

    <services>

      <service name="Improving.Samples.WCF.Web.Hello">

        <endpoint contract="Improving.Samples.WCF.Web.Hello" binding="webHttpBinding" />

      </service>

    </services>

 

    <bindings>

      <webHttpBinding />

    </bindings>

  </system.serviceModel>

 

 D'oh. Another cup of coffee, please. After a bit of reflectoring, I add in the binding extension (which seems weird, because the binding element is a subclass of StandardBindingElement, hmmm... maybe I need to add the assemblies to the WCF directory?):

  <system.serviceModel>

    <services>

      <service name="Improving.Samples.WCF.Web.Hello">

        <endpoint contract="Improving.Samples.WCF.Web.Hello" binding="webHttpBinding" />

      </service>

    </services>

 

    <bindings>

      <webHttpBinding />

    </bindings>

 

    <extensions>

      <bindingExtensions>

        <add name="webHttpBinding"

            type="System.ServiceModel.Configuration.WebHttpBindingCollectionElement, Microsoft.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

      </bindingExtensions>

    </extensions>

 

  </system.serviceModel>

 

 

OK. So, back to the browser and, Voila!

Argh. OK. So, I expected the metadata to be disabled. I didn't exactly expect the help page, but I know how to disable it via the serviceDebug behavior from a previous spelunking exercise:

  <system.serviceModel>

    <services>

      <service name="Improving.Samples.WCF.Web.Hello" behaviorConfiguration="hello">

        <endpoint contract="Improving.Samples.WCF.Web.Hello" binding="webHttpBinding" />

      </service>

    </services>

 

    <behaviors>

      <serviceBehaviors>

        <behavior name="hello">

          <serviceDebug httpHelpPageEnabled="false" includeExceptionDetailInFaults="true"/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

 

    <bindings>

      <webHttpBinding />

    </bindings>

 

    <extensions>

      <bindingExtensions>

        <add name="webHttpBinding"

            type="System.ServiceModel.Configuration.WebHttpBindingCollectionElement, Microsoft.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

      </bindingExtensions>

    </extensions>

 

  </system.serviceModel>

 

 

Almost there. I can taste it...

 

  <Fault xmlns="http://schemas.microsoft.com/ws/2005/05/envelope/none">

    <Code>

      <Value>Sender</Value>

      <Subcode>

        <Value xmlns:a="http://schemas.microsoft.com/ws/2005/05/addressing/none">a:ActionNotSupported</Value>

      </Subcode>

    </Code>

    <Reason>

      <Text xml:lang="en-US">The message with Action '' cannot be processed at the receiver,

        due to a ContractFilter mismatch at the EndpointDispatcher.

        This may be because of either a contract mismatch (mismatched Actions between sender and receiver)

        or a binding/security mismatch between the sender and the receiver. 

        Check that sender and receiver have the same contract and the same

        binding (including security requirements, e.g. Message, Transport, None).</Text>

    </Reason>

  </Fault>

 

 

So, at the end of the day, I am beginning to wonder whether I can use  from within IIS/ASP.NET. And I am beginning to believe that the answer is "No." I am sure that if it *is* "No," it is really just a "Not there yet" rather than a "Not going there." There are challenges with integrating especially the URITemplate functionality into the IIS stack, because IIS doesn't really want to talk to you unless you have an extension (e.g. .aspx, .ashx, .svc). I am guessing that they were wrestling with that limitation. If it was an explicit design decision, I would have liked to have known this sooner, e.g. in a readme.txt or, um, say, an SDR presentation. I actually commented about this when I first learned about URITemplate and friends, but the conversation took a bit of a turn, and I didn't push on it. I wish I had have, because it would have saved me some of my spare time to focus on the features that are there today. Unless I hear otherwise, I probably won't spend any more time trying to work from within a web project. Did I stop short of the prize? Or is it actually a limitation for these bits?

For now, we'll be filing this one under "Simple things should simple."

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00d83451806669e200d8353697ad69e2

Listed below are links to weblogs that reference WCF for the Web - a Smackdown:

Comments

Spot on. I also like the SoapReceiver class in WSE, which gives you direct access to the HttpContext but nicely separates SOAP calls from non-SOAP calls at the same address. Even "resourcey" services might want to handle a little SOAP from time-to-time. Again, nice job.
Not sure where I read it, but I think someone mentioned that the config file support was not there yet. I'm using a Windows Service as host and with three lines of code my service is up and and running WebServiceHost host = new WebServiceHost(typeof(PunchClockService), new Uri("http://localhost:8002/")); host.AddServiceEndpoint(typeof(IPunchClockService), new WebHttpBinding(), dataset + "/ShopService/PunchClock"); host.Open();
I think simple things should be simple too. Config sucks, and its too much of a pain. That's why we build the WebServiceHost and WebServiceHostFactory. I haven't gotten to blog about this but you've given me the motivation :) You should be able to just drop this whole thing in a .svc file and go. You'll be able to hit it at http://localhost/webapp/foo.svc/GetMessage (or whatever's appropriate for your base address) <%@ ServiceHost Language=C# Debug="true" Factory="System.ServiceModel.Web.WebServiceHostFactory" Service="Improving.Samples.WCF.Web.Hello" %> using System.ServiceModel; using System.ServiceModel.Web; namespace Improving.Samples.WCF.Web { [ServiceContract()] public class Hello { [OperationContract] [WebGet()] // Not very, um, resourcey, but... public string GetMessage() { return "Hello, Cruel World"; } } } No config. Just a single file. Simple things are simple, I hope.
BTW, I have a post up about this now: http://hyperthink.net/blog/2007/05/08/A+Brief+Aside+WebServiceHostFactory.aspx
I found this helpful http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2670799&SiteID=1
j.d.hicks, that link WAS helpful...

Verify your Comment

Previewing your Comment

This is only a preview. Your comment has not yet been posted.

Working...
Your comment could not be posted. Error type:
Your comment has been saved. Comments are moderated and will not appear until approved by the author. Post another comment

The letters and numbers you entered did not match the image. Please try again.

As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

Having trouble reading this image? View an alternate.

Working...

Post a comment

Comments are moderated, and will not appear until the author has approved them.

November 2008

Sun Mon Tue Wed Thu Fri Sat
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30            
Blog powered by TypePad

We Like