So I'm reading RESTful Web Services and I got inspired. Sounds like a great way to expose data on the web.
I've heard great things about WCF support for REST, with their new WebGet attribute and such.
So I fire up Visual C# Express and get hacking.
Amazing what one can do in 54 lines of code.
using System;
using System.ServiceModel;
using System.ServiceModel.Web;
namespace J832
{
class Program
{
static void Main(string[] args)
{
Time time = new Time();
using (WebServiceHost host = new WebServiceHost(time, new Uri("http://localhost:8080/")))
{
host.AddServiceEndpoint(typeof(ITime), new WebHttpBinding(), "time");
host.Open();
Console.WriteLine("enter to close...");
Console.ReadLine();
}
}
}
[ServiceContract]
public interface ITime
{
[OperationContract, WebGet]
DateTimeCount GetTime();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Time : ITime
{
public DateTimeCount GetTime()
{
return new DateTimeCount(DateTime.Now, m_count++);
}
private int m_count;
}
[Serializable]
public class DateTimeCount
{
public DateTimeCount(DateTime now, int count)
{
Now = now;
Count = count;
}
public DateTime Now { get; set; }
public int Count { get; set; }
}
}
And we open a browser to http://localhost:8080/time/GetTime:
<DateTimeCount xmlns="http://schemas.datacontract.org/2004/07/J832" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<_x003C_Count_x003E_k__BackingField>0</_x003C_Count_x003E_k__BackingField>
<_x003C_Now_x003E_k__BackingField>2008-02-04T23:28:21.987561-08:00</_x003C_Now_x003E_k__BackingField>
</DateTimeCount>
Isn't that *cough* *choke* pretty *gag*.
I suddenly had a flash of nostalgia...of longing...for Xaml.
Xaml: that wonderful language where document-object mapping is implicit, trivial, and human-understandable.
(I'd love to link to Our 7 Goals of Xaml introduction but the link is dead. Help, Rob?)
Thankfully, XamlWriter is publicly available and WCF is insanely, gratuitously, wonderfully extensible.
- WCF services have endpoints.
- Endpoints have contracts.
- Contracts have operations.
- Operations have behaviors.
- Behaviors have formatters.
- Formatters can have custom messages--for instance, XamlMessage.
Thanks to Maheshwar Jayaraman for the post (and sample) on his old blog that got me started.
The code is a bit longer, but it's mostly boiler plate:
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
using System.Windows.Markup;
using System.Xml;
[assembly: XmlnsDefinition("http://schemas.j832.com/demo/2008", "J832")]
namespace J832
{
class Program
{
static void Main(string[] args)
{
Time time = new Time();
using (WebServiceHost host = new WebServiceHost(time, new Uri("http://localhost:8080/")))
{
ServiceEndpoint endPoint = host.AddServiceEndpoint(typeof(ITime), new WebHttpBinding(), "time");
foreach (OperationDescription operationDescription in endPoint.Contract.Operations)
{
operationDescription.Behaviors.Add(new MyOperationBehavior());
}
host.Open();
Console.WriteLine("enter to close...");
Console.ReadLine();
}
}
}
[ServiceContract]
public interface ITime
{
[OperationContract, WebGet]
DateTimeCount GetTime();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Time : ITime
{
public DateTimeCount GetTime()
{
return new DateTimeCount(DateTime.Now, m_count++);
}
private int m_count;
}
[Serializable]
public class DateTimeCount
{
public DateTimeCount(DateTime now, int count)
{
Now = now;
Count = count;
}
public DateTime Now { get; set; }
public int Count { get; set; }
}
public class MyOperationBehavior : IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { }
public void ApplyDispatchBehavior(
OperationDescription operationDescription,
DispatchOperation dispatchOperation)
{
dispatchOperation.Formatter = new MyMessageFormatter();
}
public void Validate(OperationDescription operationDescription) { }
}
public class MyMessageFormatter : IDispatchMessageFormatter
{
public void DeserializeRequest(Message message, object[] parameters) { }
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
return new XamlMessage(result);
}
}
public class XamlMessage : Message
{
public XamlMessage(object value){ Value = value; }
public override MessageHeaders Headers
{
get { return new MessageHeaders(MessageVersion.None); }
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
XamlWriter.Save(Value, writer);
}
public override MessageProperties Properties{ get{ return new MessageProperties(); } }
public override MessageVersion Version{ get{ return MessageVersion.None; }}
public object Value { get; private set; }
}
}
And (drumroll) the output from http://localhost:8080/time/GetTime:
<DateTimeCount Now="2008-02-04T23:53:18.884273-08:00" Count="14" xmlns="http://schemas.j832.com/demo/2008"/>
Isn't that pretty? The XmlnsDefinition assembly attribute is the icing that gives you the pretty xmlns.
Do other folks love Xaml? Do you wish it was available outside PresentationFramework.dll?
Let me know. I'm thinking of starting a campaign.
Hear that, Rob? :-)
9 comments:
You are declaring automatic fields when you write
public DateTime Now { get; set; }
public int Count { get; set; }
In this case the value is stored in automatically generated fields, named
<Count>k__BackingField
and
<Now>k__BackingField;
The wcf-serializer just seems to use the fields instead of the properties what finally leads to this "pretty" xml result.
XAML as a serialization format is an interesting concept though.
I'm as yet undecided, but I think I'm leaning towards MonoRail or Asp.net MVC for building RESTFul services instead of WCF.
Very cool... I love XAML. You should know much more about it, but there are some hints that it has a great career coming outside of WPF (and WF):
http://blogs.zdnet.com/microsoft/?p=1159
When will we see a XAML based IoC container?
Release that behavior as an independent assembly, whip it up in a config and that boilerplate goes away and it becomes really pretty.
Yep, I'm completely baffled why XamlReader/Writer is in a WPF assembly. I mentioned this in my recent post here: http://kentb.blogspot.com/2008/01/xaml.html
That said, I think you'd be hard-pressed to get this changed now.
From what I know there was to be a XAML specification, separate from WPF or anything else... But don't know if that ever materialized...
As for IoC using XAML, there's already DynamicResource, what else would you need?
WCF gives you better control of the XML using the [DataContract] and XmlSerializer programming models. It supports [Serializable] but autogenerates names.
You could get much better results if you had annotated as
[DataContract]
class Foo
{
[DataMember]
DatewTime Now { get; set; }
[DataMember]
int Count { get; set; }
}
Kevin, is sweet! You beat me to the punch! Think of the posibilities of consuming straight XAML from your client!
Pretty sweet!
Think of the posibilities of consuming straight XAML from your SERVER!
Even sweeter for smart clients!
aliadams
Post a Comment