Getting WCF to talk ordinary HTTP to a browser

This is an exercise in driving nails into the coffee table with your shoe. The goal isn’t really all that obviously beneficial and the tool isn’t the expected tool for the job. WCF wants to speak SOAP to SOAP aware clients. With the expansion to support a REST API with System.ServiceModel.Web, you can get a WCF service to talk to a browser. HOWEVER

* The browser doesn’t serialize complex objects to a C# like data type system on Request or Response. Instead you deal primarily in a raw Stream.
* Some browsers don’t speak XHTML (they will render it if you call it text/html, but MSIE will render xhtml/application as XML), so you can’t just return an X(HT)ML payload.
* WCF used this way is a “bring your own view engine” framework. I chose SharpDom for this exercise. It seems like it should be possible to support returning a SharpDom return value that serializes to XHTML with a type of text/html, but I don’t know how to do that.
* MVC already solves a lot of similar problems.

BUT with WCF you get some of those WCF features, like umm, well, when you have a browser client a lot of features aren’t avail (e.g. fancy transaction support, callbacks, etc), but you can still do fancy thinks like instancing, and supporting a HTML browser, JSON and WCF interface all on top of mostly the same code.

Just serving a page is fairly easy. Turn on web support in the config (same as any REST enabling, see end of post),

[WebGet]
public Stream HomePage(){ 
            //Return a stream with HTML
            //... I have skipped the view engine, I used SharpDom
            MemoryStream stream = new MemoryStream();
            TextWriter writer = new StreamWriter(stream, Encoding.UTF8);
            new PageBuilder().Render(model, writer);
            writer.Flush();
            stream.Position = 0;
            return stream;
}


What will the URL look like? Well in devepment in Win 7, if you don’t have admin rights, it will be something like:

http://localhost:8732/Design_Time_Addresses/HelloWorld/web/HomePage

The http://localhost:8732/Design_Time_Addresses/ is the address that a non-admin can register. It looks like you can’t register 8080.

The /web/ part is because in my endpoints in config (below), the endpoint is “web”

Also notice you have to set an encoding (and I suppose you’ll want that to match what the HTML meta tag says)

[WebInvoke(Method = "POST")]
public Stream AnotherPostBack(Stream streamOfData)
{
StreamReader reader = new StreamReader(streamOfData);
String res = reader.ReadToEnd();
NameValueCollection coll = HttpUtility.ParseQueryString(res);
//Return a stream of HTML
}

To invoke the above, use an METHOD of POST and an action of

http://localhost:8732/Design_Time_Addresses/HelloWorld/web/AnotherPostBack

And finally, use a web friendly host in your console app

using (WebServiceHost host = new WebServiceHost(typeof(HelloService)))
{
host.Open();
Console.ReadLine();
}

http://stackoverflow.com/questions/1850293/wcf-rest-where-is-the-request-data

Also, you can post back to this kind of operator… but for the life of me I can’t figure out how to get the Content. I can see the headers, I can see the content length, but I can’t get at the stream that holds the post’s content.

(This StackOverflow Q & A implies that to get the raw content, you have to use reflection to inspect private variables: )

[OperationContract(Action = "POST", ReplyAction = "*")]
[WebInvoke(Method = "POST")]
public Stream PostBack(Message request)
{
}

Obviously, cookies and URL params are just a matter of inspecting the IncomingRequest.

And the config:

<system.serviceModel>
    <services>
      <service name="WcfForHtml.HelloService" behaviorConfiguration="TestServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8732/Design_Time_Addresses/HelloWorld"/>
          </baseAddresses>
        </host>
        <endpoint address="web"
                  binding="webHttpBinding"
                  contract="WcfForHtml.HelloService"
                  behaviorConfiguration="webBehavior">
        </endpoint>
      </service>
    </services>
      <behaviors>
        <!--SERVICE behavior-->
        <serviceBehaviors>
          <behavior name="TestServiceBehavior">
            <serviceMetadata httpGetEnabled="true" />
            <serviceDebug includeExceptionDetailInFaults="true"/>
          </behavior>
        </serviceBehaviors>
        <!--END POINT behavior-->
        <endpointBehaviors>
          <behavior name="webBehavior">
            <webHttp/>    
          </behavior>
        </endpointBehaviors>
      </behaviors>
  </system.serviceModel>

Posted in wcf

WCF + .NET 2.0

I’ve got an application that is stuck at .NET 2.0 for a while. But I don’t do something to demonstrate WCF is useful, it will be stuck at 2.0 for much longer.

The Use Case: Unit Testing.
WCF opens your application up to more clients. If you think your application has only one client, your wrong, you application should have unit tests and the unit tests constitute a client, in addition to you web forms or windows forms application.

WCF in one Assembly, Business Logic in the Other.
This works fine for the service class. The .svc file is a wrapper around some class that implements an interface with the relevant attributes. I haven’t figured out a way to do this without using a wrapper pattern. However, not so serious a problem because a service is probably going to be designed differently than the underlying business object– probably more stateless for one.

The complex data types used for the data transfer objects are more problematic. The WCF way is to put a [DataContract] attribute on them. This means either putting a wrapper around your data types (i.e. your Customer object, the Order object, etc) or moving the WCF attributes into the business logic class’s assembly. Not possible if I want to leave the business logic tier at .NET 2.0.

Fortunately, .NET 3.5 SP1 supports POCO serialization, i.e. WCF will turn any plain old clr object into xml on the wire. That means you don’t need to put the DataContract attribute on your 2.0 business objects, or wrap the business object in a 3.5 wrapper.

Choice of Host.
If your stuck with .NET 2.0, you’re probably stuck with IIS 5/6 and Server 2000/2003. That means no WAS. So hosting means web services wcf style over http. Unless you write your own host, probably using a windows service.

Webservices and WCF- eatting one’s own dogfood

The official line for WCF and webservices is that they are for interop especially between organizations and between different technology stacks, e.g. Java and .NET or COBOL and .NET.

Who wants to build an open API when no one has asked for it? Who will ask for an open API if one doesn’t already exist?  For WCF/webservices to happen at all you have to image a use case for these that would be useful now.

Three reason why you’d wan’t to consume your own webservices:

Javascript to .NET interop.  This allows for Aptana driven development against a C# application.  This becomes especially compelling if you have webservices returning JSON,  and RSS, because the client will be simpler to write.

Testability.  The webservices API is more testable than the webforms that do much the same thing.

Data access.  XML is a datatype too.  It can be handy to have one more dataformat in the data monkey’s toolbox.

Compatibility with future versions. WCF especially- The web service as a programming model might be low performance, but it is remarkably resiliant to changes in implementation.  So much thought has gone into defining an interface that works with everyone, it will even work with that foreign application called “Your Application, version next”