ServiceMetadataExtension refactoring story

| No Comments | No TrackBacks

I had been fixing several bugs in our WCF code these months. Yeah, I kept silent just because it is too boring topic to blog :p

One of the most problematic one was ServiceMetadataExtension support, which is for WSDL output on the service site (the ones you get when you access to http://yourwebsite/yourservice.svc?wsdl). Yes, surprisingly. You would not understand why that is so problematic.

(0) (re)introduction

For public API wise, I once wrote an entry-level entry for entrypoint to service metadata support last year. I didn't give any explanation on its internals at all. Now it's time.

A simple code for hosting a WCF service with metadata support would look like:

var host = new ServiceHost (typeof (FooService), new Uri (uri));
host.AddServiceEndpoint (typeof (ITestService),
    new BasicHttpBinding (),
    new Uri ("", UriKind.Relative));
host.Description.Behaviors.Add (
    new ServiceMetadataBehavior () { HttpGetEnabled = true });
host.Open ();

Today, in the example above, what actually creates a working service listener is only the last line: host.Open (); only the last line matters. I explain what it's doing there, step by step.

(1) ServiceHostBase and Binding

First, I'll explain what ServiceHostBase supports and what you can do only with Binding.

Basically, ServiceHost(Base) manages more than one service "endpoint". You can call "AddServiceEndpoint" on the same host multiple times. A service endpoint is a set of an "endpoint address", a "binding" and a "service contract". You can implement multiple service contract interface within a service type (FooService) and serve requests to different interfaces at different endpoint URIs (addresses), using the same or different binding. A binding can be things like BasicHttpBinding, NetTcpBinding, WebHttpBinding, WSHttpBinding (not supported in mono yet) or anything that derives from Binding.

You might know that Binding class has a method that creates an IChannelListener object that is to actually listen for requests. Actually, to receive requests in Message form, you don't have to even use ServiceHost. Instead, you can do this:

var binding = new BasicHttpBinding ();
IChannelListener<IReplyChannel> listener =
   binding.BuildChannelListener<IReplyChannel> (
      new Uri ("http://localhost:8080/"));
listener.Open ();
IReplyChannel ch = listener.AcceptChannel ();
ch.Open ();
RequestContext ctx = ch.ReceiveRequest ();

RequestContext has a request Message that is created from the HTTP SOAP request it received. You can use Reply(Message) method to return a message in whatever form you want.

You wouldn't find it very useful. You'd rather use strongly-typed services instead so that you don't have to get messes by Message object. Also you wouldn't like to call ReceiveRequest() every time explicitly. Hence, there is ServiceHost(Base). It handles those tasks.

(2) ChannelDispatcher and EndpointDispatcher basics

When ServiceHostBase.Open() is called, the host creates a set of "ChannelDispatchers" in the host. A channel dispatcher is created for each Binding used in the ServiceHostBase (actually for each of its service endpoints).

A ChannelDispatcher manages an IChannelListener created from Binding.BuildChannelListener<TChannel> method. TChannel can be something other than IReplyChannel, but I don't explain it here (not primary topic today).

A ChannelDispatcher holds one or more "endpoint". In ServiceHostBase, there could be multiple ServiceEndpoints and they have a Binding and an EndpointAddress. Actually the Binding instance can be shared, and in such case, those ServiceEndpoints that shares the same Binding also shares the same ChannelDispatcher.

In ChannelDispatcher, the set of endpoints is represented as Endpoints property and an endpoint becomes an EndpointDispatcher. EndpointDispatcher is hence bound to a contract. When a request to the channel listener arrives (e.g. HTTP request), the ChannelDispatcher has to determine which "endpoint" (EndpointDispatcher) should process the request. Usually it is determined by its ContractFilter and AddressFilter properties, and since usually service endpoint URIs differ, it does not matter much (Use "/foo" for IFooService and "/bar" for IBarService.

... well, I wrote "basics". Yes, it is basic part. The core part starts from here.

(3) IMetadataExchange, ServiceMetadataBehavior and ServiceMetadataExtension

With BasicHttpBinding, an IChannelListener is created for HTTP scheme, and its listening URI is typically a local HTTP URI. In mono, it is either done by ASP.NET (xsp) or HttpListener (non-ASP.NET). In both cases, the services blocks the listening URI, and if we simply try to serve WSDLs using another HttpListener, there is no more room. Since WSDL requests could be sent to the same URI of the service itself, typically only differentiating the query parameter, there sould be some trick. (Do you understand it's getting messier?)

Another complication factory is that there is ServiceMetadataBehavior and ServiceMetadataExtension that have some Binding and Uri properties and show capability of handling "metadata exchange" requests. So it's not only about WSDL. Its endpoints must be exposed at users' will.

Interestingly, MetadataExchangeBindings class exposes a couple of static methods that creates a Binding for mex endpoints. And they are actually used by ServiceMetadataExtension, that is an IExtension for ServiceHostBase and realizes ServiceMetadataBehavior's requirements. Since Binding instance are different, it can create another IChannelListener, which typically has the identical listen URI as the service endpoint itself has.

The different Binding instance results in different ChannelDispatcher in the ServiceHostBase. I was originally aware of the fact that when the ServiceHostBase is opened there are two ChannelDispatchers (the reason came later). Different channel disptatchers have (again) different IChannelListener instances. And they still point to the same listen URI.

That's problematic. It makes request dispatching difficult. To make it worse, ChannelDispatcher has MessageVersion property. If they resided in the same ChannelDispatcher, it would have been easier since we could use message filters to select appropriate EndpointDispatcher. HTTP request interpretation varies dependin on the target MessageVersion. So the target ChannelDispatcher and EndpointDispatcher must be selected before IChannelListener.ReceiveContext() is done.

(4) HTTP listener manager

Hence we have to create another management layer for HTTP channel listener to reuse the same HttpListener (and some equivalent management layer for ASP.NET). Actually when Mainsoft hackers were working on it, they were aware of this issue, and created a management layer. It mostly worked until we reached the point that we have to handle more strict differentiation.

Basically, we determine if the request is GET and if the target URL matches the wsdl GET URL, then it is for WSDL. Actually there is ServiceDebugBehavior so we also handle it (if you omit "?wsdl" query parameter in your HTTP request, you'll see some "help page" for the service. It is what ServiceDebugBehavior is for).

BUT, we can't bindly determine such requests as for WSDLs. Do you remember there is WebHttpBinding? With this binding, you can access services by UriTemplate, and it is todally done by GET request (with no request stream). Whenever applicable, we should handle requests to the RESTful services within the binding. Interestingly, WebHttpBinding uses WebHttpBehavior to adjust endpoint behaviors, and it actually raises its EndointDispatcher's FilterPriority(!). So IF the endpoint dispatcher is in the same ChannelDispatcher as ServiceMetadataBehavior yields, there was no problem. Sadly, WebHttpBinding is, again, a different binding that ServiceMetadataBehavior gives, so it creates different ChannelDispatcher (as BasicHttpBinding does).

To get the higher priority settings working, we have to "hack" the order of searching appropriate ChannelDispatcher to dispatch an HTTP request, by its Endpoints (EndpointDispatchers).

All of those behaviors cannot be completely done within WCF public API. So, ServiceMetadataBehavior and ServiceMetadataExtension are special. You cannot create functionally-equivalent one without giving up some aspects (e.g. it would be able to be done if you don't allow hosting WSDL on the same URI as the service is listening).

(5) done

It had been a longstanding issue that attacked me over and over again (fix->regress->fix->regress...), and history may repeat, but I rewrote the HTTP listener stack and relevant stuff last week, it should work today, hopefully in more reliable state. And since that fixes I feel much comfortable than those annoyed days in my hacking life :)

initial System.Xaml work

| No Comments | No TrackBacks

These days I have been running away from WCF and hacking new System.Xaml.dll which is new in .NET 4.0 last week. I needed some break and fresh air.

It is already in mcs tree, and not a few of them are implemented and already working, though there's a lot of things left to do.

In .NET land, there seems to be some class libs such as WPF and new Workflow that depend on it. And I'm guessing it could be reused for some sort of my own designing tool.

My primary work area is still WCF so I won't be able to finish it soon, but I hope I get it practically working by the next 2.8 release (which is not in certain scope yet).

Mono WCF Advent Day 13: WCF 4.0 Routing

| 1 Comment | No TrackBacks

While we don't have some significant parts of WCF 3.0 stack such as working message security, we have some new stuff, like WebHttpBinding I explained on Day 9. It also applies to WCF 4.0. We have new routing stack.

WCF router support is only in SVN trunk, in System.ServiceModel.Routing assembly, mostly in System.ServiceModel.Routing namespace. Note that .NET 4.0 is still in beta, so things may change as .NET 4 development goes on.

Router usage

Message router is to dispatch messages that it received from clients, to certain servers. A WCF router is a WCF service for router client users, while the router works as a WCF client for routed services (which may not be WCF).

Routing design is almost simple: a WCF router service has a table of a message filter that is used to test if a message matches it or not, and a set of endpoints to dispatch messages it received. The table is represented as MessageFilterTable<MessageFilter,IEnumerable<ServiceEndpoint>>.

(Usually just one service endpoint is enough; simplex or duplex channels may dispatch messages to more than one service endpoints.)

Message router often has to rebuild the input message, like, adjusting message versions and To message header.

I haven't prepared an entire routing example (I tested it with MS sample in 4.0), but router usage would look like:

var host = new ServiceHost (typeof (RoutingService));
host.AddServiceEndpoint (
  typeof (IRequestReplyRouter),
  new BasicHttpBinding (),
  new Uri ("http://localhost:8081/router"));
var config = new RoutingConfiguration ();
var clientEndpoint = new ServiceEndpoint (
  ContractDescription.GetContract (typeof (IRequestReplyRouter)),
  new BasicHttpBinding (),
  new EndpointAddress ("http://localhost:8080/service"));
var list = new List<ServiceEndpoint> ();
list.Add (clientEndpoint);
config.FilterTable.Add (new MatchAllMessageFilter (), list);
host.Description.Behaviors.Add (new RoutingBehavior (config));

Internals

There is not many things to explain for WCF routing, so I'd explain its internals instead so that some people might want to write similar code for several purposes.

To host a routing service, RoutingService is used. It has four interfaces for several channel interface types;

  • IRequestReplyRouter for typical request-reply channels
  • ISimplexDatagramRouter for input/output channels
  • ISimplexSessionRouter for input/output sessions channels
  • IDuplexSessionRouter for duplex session channels

To add routing with a specific table, you'll first have to create the table. It is represented as FilterTable property in RoutingConfiguration class. Once you have created a configured RoutingConfiguration instance, then use RoutingBehavior object, which is of IServiceBehavior interface (a behavioral extension to ServiceDescription, see Day 5 for quick introduction to "service behaviors") and takes RoutingConfiguration as its constructor argument.

Once you have set routing configuration in above manner, then what to do next is to call AddServiceEndpoint() with (1) an interface type I listed above, (2) a binding for the router "(as a) service", and (3) an endpoint address for the router "(as a) service". (1) must be determined as to comply with the routed servers' requirements, and (2) and (3) represent the endpoint information for routers' client (and of course, a client must comply with (1) to properly communicate with the router).

Those four routing interfaces I listed above, all have Message as its one input argument (and output for IRequestReplyChannel). Since the contracts are very generic, a routing service can handle any communication between the client and the routed server (i.e. since they just take Message, it involves no typed serialization regardless of the contract of the routed service).

When a RoutingBehavior is called its ApplyDispatchBehavior() with a ServiceHostBase argument, it adds RoutingExtension to the service host. Then (1) it updates its service "instance context provider" (IInstanceContextProvider) that instantiates a RoutingService (which has no public constructor) and sets the routing configuration to it, and (2) creates a set of clients for the dispatched services internally.

There is another "endpoint behavior" SoapProcessingBehavior. It takes part in the router-to-service communication and adjusts messages (as I explained above e.g. updating message versions). You don't have to deal with it.

It is in very early stage and in fact I only tried request-reply style router.

Bonus

Our Routing stack should work on .NET 3.5 too. Try "make" under mcs/class/System.ServiceModel.Routing - it will build mcs/class/lib/net20/System.ServiceModel.Routing.dll i.e. for 2.0 profile. There is nothing new I depended for its underlying layer.

Mono WCF Advent Day 12: NetPeerTcpBinding

| No Comments | No TrackBacks

With related to NetTcpBinding, I have also implemented NetPeerTcpBinding, which is based on peer-to-peer communication.

NetPeerTcpBinding involves a lot of complicated things, so I'm not likely to explain a lot.

Peer to peer channel overview

NetPeerTcpBinding itself is a binding for duplex messaging with binary message encoders (like NetTcpBinding) and its own transport, namely PeerTransportBindingElement. It also involves a "peer resolver" which is used to manage peer node statuses.

PeerTransportBindingElement is a transport binding element like HttpTransportBindingElement or TcpTransportBindingElement. While HTTP endpoint is a typical http URI (locally it is most likely only about the port), and TCP endpoint is about an IP endpoint (I forgot to mention yesterday, but its URI is like "net.tcp://localhost:808"), peer transport endpoint is about a "mesh" name which is handled by a "peer resolver". Its URI looks like "net.p2p://mymesh/" . Messages are transmitted to all the "peer nodes" in the specified peer "mesh".

(If you read PeerTransportBindingElement API, you'd notice that it takes an IP endpoint for listening peer connections. But it is not the endpoint - peer transport indirectly uses it, unlike an IP endpoint for TCP endpoint.)

Actually, a peer resolver, which is represented as PeerResolverBindingElement, is more inportant to know the actual peer transport destination. On .NET there are (nominally) two ways to "resolve" peer nodes to transmit messages: (1) PNRP, represented as PnrpPeerResolverBindingElement is Windows peer node resolution protocol, and (2) custom resolver, represented as PeerCustomResolverBindingElement, is resolver-instance-by-user peer resolver with IPeerResolverContract and CustomPeerResolverService API.

With PNRP you don't have to specify anything to resolve peers. It is however not available in Mono (PNRP is Windows specific and I haven't spent any time to implement it). With custom resolver, you have to (only) specify a service endpoint (Binding and EndpointAddress) to IPeerResolverContract service, which is typically CustomPeerResolverService.

Having your host is simple. Just create a ServiceHost with CustomPeerResolverService and add a service endpoint for IPeerResolverContract.

Usage example

(NOTE: As it depends on NetTcpBinding and it has some blocking issues right now, this binding does not likely work fine either.)

I haven't written any cool net-peer application beyond simple tests. Instead, I used chat application which used to be the top Google results for "WCF application". The server isn't really a peer channel server but rather a custom peer resolver, but the clients show basic use of net-peer communication.

The example code makes use of PeerNode and its interface IOnlineStatus.

Implementation internals

The public peer resolver API does not tell much, but its internals are complicated. It has to provide several operations such as, register node to the target mesh (Register), resolve nodes in the target mesh (Resolve), updates node availability statuses (Refresh) and unregister the node from the mesh (Unregister).

Microsoft has a dedicated Windows protocol documentation. The expected peer resolver mechanism is documented as [MC-PRCR] and [MC-PRCH].

In .NET, CustomPeerResolverService internally uses PNRP to implement IPeerResolverContract, So, while "nominally" custom peer resolver is one of the two peer resolver kinds, there is actually only PNRP on .NET.

In Mono, I created BasicHttpBinding-based ad-hoc peer resolver service that is launched within a mono process that uses custom resolvers (the internal "LocalPeerResolverService" class in System.ServiceModel.dll implements the service). It checks port 8931 availability, and if it is already used then it premises as if it were occupied by another custom resolver of our implementation. This behavior may change in future versions.

For paranoiac

It is a trivia I found during NetPeerTcpBinding hacking. You cannot expose WSDL for IPeerResolverContract using ServiceMetadataBehavior (see Day 5) because of duplicate global element for 'http://schemas.microsoft.com/net/2006/05/peer:Update' . 99.999% of WCF users wouldn't have to do it though (say, you don't have to implement CustomPeerResolverService).

The reason for this is complicated: it is because RegisterResponseInfo and UpdateInfo have conflicting return element name "Update" (in the same XML namespace) due to its MessageBodyMemberAttribute. That's my guess though - as IPeerResolverContract methods use message contracts for its argument and return value types. But as the contract there wouldn't be other possibilities so it should be almost precise.

The above story is almost nothing for you. But this kind of faulty contract design could happen by chance and it cannot be checked very obviously. The only way I can suggest to try to avoid such problem is to check WSDL generation frequently (like, every time you change the message contract).

Mono WCF Advent Day 11: NetTcpBinding

| No Comments | No TrackBacks

Until today I have explain only two Bindings (or three): BasicHttpBinding and WebHttpBinding (and CustomBinding if it counts). But as I implied earlier, we have more. Today I'd explain TCP transport and NetTcpBinding and couple of new things.

In short, NetTcpBinding is WCF-only, non-interoperable binding based on binary messages and TCP transport.

Binary XML serialization

"Binary XML" is not really XML. It is fake, a buzzword. It actually means, a binary representation based on XML Infoset (or something, depending on what it would describe). In WCF land, binary XML usually means an XML binary format used by XML reader and writer from XmlDictionaryReader.CreateBinaryReader() and XmlDictionaryWriter.CreateBinaryWriter().

While we have corresponding implementation since 2005, its data format has been recently published as [MC-NBFX] as part of Microsoft's Windows Protocol documentation.

In WCF, there is BinaryMessageEncodingBindingElement that uses this binary XML representation to serialize and deserialize messages. You can explicitly use it as message encoder part of your binding (using CustomBinding) as I did on Day 3. NetTcpBinding uses this one without customization.

Request/Response vs. Duplex

Message transport protocol also involves its own binary representation, but I have to explain "channel types" in prior. Until today I haven't explain about channel types, in other words types of IChannel interface variety - as BasicHttpBinding and WebHttpBinding are both for request-reply style communication. WCF is not only about it - it actually supports:

  • request-reply messaging: represented as IRequestChannel and IReplyChannel. A client sends a request to a server, then the server receives it and sends reply back to the client, and the client receives it.
  • simplex messaging: represented as IOutputChannel and IInputChannel. A client sends a message to a server, then the server receives it. No further communication is performed.
  • duplex messaging: represented as IDuplexChannel. Either of the communication sides may perform simplex messaging.

They may also involve sessions and in such case they become IFooSessionChannel (e.g. IRequestSessionChannel). To know which kind of messaging is supported by the Binding you are using, you can use CanBuildChannelFactory<TChannel>() method for clients or CanBuildChannelListener<TChannel>() method for servers.

It may look strange, but duplex messaging could be still used to support service method that has both invocation parameters (request) and return value (reply).

TCP binary data exchange

Back to NetTcpBinding, it supports two kinds of above, under different configuration; NetTcpBinding has TransferMode property and it indicates the communication channel style. If the property value is TransferMode.Streamed, then it creates request-reply type channel. If it is TransferMode.Buffered, then it results in duplex channel. While duplex channel could be used for RPC-style service calls, the underlying communication protocol becomes different. So you have to adjust the transfer mode between client and server.

The communication protocol is also documented, as [MC-NMF] and some related docs in Windows Protocol documentation.

NetTcpBinding example

Here is an example pair of NetTcpBinding client and server, using buffered == duplex communication mode.

[NOTE] It turned out that I brought some regressions which broke some binary message communication. It has been a while since I have implemented a couple of months ago. I've been fixing some bugs these days.

// client
using System;
using System.Xml;
using System.ServiceModel;
using System.ServiceModel.Channels;

public class Test
{
  public static void Main ()
  {
    var binding = new NetTcpBinding ();
    binding.Security.Mode = SecurityMode.None;
    var endpoint = new EndpointAddress ("net.tcp://localhost:8080/");
    var proxy = new ChannelFactory<IFoo> (binding, endpoint).CreateChannel ();
    Console.WriteLine (proxy.Echo ("TEST FOR ECHO"));
    Console.WriteLine (proxy.Add (1000, 2000));
  }
}

[ServiceContract]
public interface IFoo
{
  [OperationContract]
  string Echo (string msg);

  [OperationContract]
  uint Add (uint v1, uint v2);
}
// service.cs
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

public class Tset
{
  public static void Main ()
  {
    ServiceHost host = new ServiceHost (typeof (Foo));
    NetTcpBinding binding = new NetTcpBinding ();
    binding.Security.Mode = SecurityMode.None;
    var uri = new Uri ("net.tcp://localhost:8080");
    host.AddServiceEndpoint ("IFoo", binding, uri);
    host.Open ();
    Console.WriteLine ("Hit [CR] key to close ...");
    Console.ReadLine ();
    host.Close ();
  }
}

[ServiceContract]
public interface IFoo
{
  [OperationContract]
  string Echo (string msg);

  [OperationContract]
  uint Add (uint v1, uint v2);
}

class Foo : IFoo
{
  public string Echo (string msg)
  {
    return msg;
  }

  public uint Add (uint v1, uint v2)
  {
    return v1 + v2;
  }
}

Limitation

.NET NetTcpBinding is actually capable of handling some complicated things such as message security, but we don't provide message security support yet. If you have NetTcpBinding configured to use message security, NetTcpBinding.Security.Mode has to be set as None.

Mono WCF Advent Day 10: ASP.NET AJAX Integration

| No Comments | No TrackBacks

Well, yes, it continues a bit more...

Yesterday I explained WebHttpBinding which is dedicated to REST world. It is however just one aspect of the new REST support in .NET 3.5. Here is another one - you can invoke remote WCF services from ASP.NET AJAX.

Javascript proxy generator

What's done here is, basically the same as what ASP.NET AJAX do for System.Web.Services asmx support. In asmx integration land, there is Javascript proxy generator that accesses to the remote service and returns server responses back to Javascript land. The Javascript proxy source is retrieved at http://your.host.name/service.svc/js or http://your.host.name/service.svc/jsdebug (with debugging info).

The implementation is in ProxyGenerator class in ASP.NET AJAX assembly (System.Web.Ext.dll). By using ProxyGenerator.GetClientProxyScript() method, you can see how the raw Javascript proxy code is generated from your service contract, without actually deploying your service at your host.

It matches the Javascript code that you can see at somewhere like: http://your.host.name/service.svc/jsdebug

WebScriptEnablingBehavior

The next question is, how to provide such Javascript proxy at ./js or ./jsdebug for each service (endpoint). When there are two or more services, there will be two or more js proxies (foo.svc/js and bar.svc/js).

To resolve this, there is "WebScriptServiceHostFactory" which adds support for those js proxies for each service endpoint. It internally uses WebScripeEnablingBehavior which is derived from WebHttpBehavior. When WebScriptEnablingBehavior is attached to a service endpoint, it adds a special request "dispatcher" (precisely, EndpointDispatcher) to the host to handle requests to ./js or ./jsdebug as to return js proxy string.

I don't explain the mechanism a lot here, but in case you are curious, ChannelDispatcher, EndpointDispatcher and DispatchRuntime have a lot of customizible things at service host.

(In my previous post, I didn't explain much about WebHttpBehavior, an implementation of IEndpointBehavior. It actually plays the primary role on the REST binding support such as, setting "message formatter" to serialize request parameters using JSON serializer, to bind to and generate parameters from "URI template", etc. etc.)

Anyways, now we have a dedicated endpoint for Javascript for each service endpoint from the service instance. The service instance should be adjusted to use WebHttpBinding which is configured to use JSON as its message format so that the service can communicate with the JS proxy running in users' browser.

ASP.NET hosting

The entire service host setup above is valid to standalone ServiceHost too. But it does not make sense to do so - the primary purpose of those js proxies is reference from ASP.NET AJAX web pages. Hence there is almost no use for standalone hosting.

I don't write a lot to explain how to use <asp:ServiceReference > and proxies in your ASP.NET AJAX web page (Javascript) here - our bug #525575 shows simple usage.

Xmas!

| No Comments | No TrackBacks

It's 5am here so I don't think I can give long explanation on my hack so I'll do it in later days, but here is one of my hobby hack of this year: visual MIDI player based on Moonlight desktop.

mldsp-gtk-2009xmas.png

Sources and brief introduction are on github.

Merry Christmas!

Mono WCF Advent Day 9: RESTful WebHttpBinding

| 6 Comments | No TrackBacks

REST support has been introduced in .NET 3.5. It includes JSON support for message serialization, and support for RSS and Atom for consuming web feeds (as serialization contract). It is implemented as WebHttpBinding in System.ServiceModel.Web.dll.

Unlike some of the topics I wrote earlier, our REST support compatibility for .NET 3.5 is almost feature complete (yes I prefer RESTians than SOAPy people).

URI Templates

REST services have "resources" identified by URLs, like: http://localhost/ShoppingService/Products/by-name/RupertsDanceLesson_DVD

While HTTP transport stack is reusable, the existing BasicHttpBinding does not fill such needs - the destination of its request is identified by SOAP request header or HTTP request content body. A dedicated message encoder is required.

The alternative mechanism used for processing such requests in WebHttpBinding is "UriTemplate" which is a syntax based on URI but includes expressions like '{x}'. For example: "/ShoppingService/Products/by-name/{name}"

When a client is to send a request with certain set of parameters, the client first determines the request URI by filling dynamic parts of the URI template. Then the client sends the request to the service. When the service received a request, it tries to determine which operation in the service should handle it, and extract parameter values from the actual request URI. There won't be files at the corresponding destination URI. It's all virtual.

JSON reader, writer, serializer

Requests in WebHttpBinding are created as an URI. Responses in REST services are often returned as JSON. With WebHttpBinding, you can read and write JSON in certain structure - since WCF Messages are serialized as "XML", which strictly means, XmlDictionaryReader/Writer, the JSON input and output are also represented as the XML reader and writer. I don't explain the details, but you can start reading from JsonReaderWriterFactory.

There also is another specialized type for JSON serialization i.e. DataContractJsonSerializer. It reads and writes JSON instead of XML.

JSON serializer could be also used for serializing complex request parameters.

Of course, WebHttpBinding does not always use JSON. Rather, it uses POX (plain old xml) by default.

RSS and Atom serialization

Another typical practice in REST world is use of syndication feeds such as RSS and Atom. WCF 3.5 added support for serialization of those feeds. In System.ServiceModel.Syndication namespace there is a couple of types for data contract serialization of feeds and feed items.

Operations using WebGet and WebInvoke

With WebHttpBinding the existing OperationContractAttribute is not sufficient to provide how to host services. A URI template must be associated to each operation. WebGetAttribute or WebInvokeAttribute are used onto an operation method to bind URI template. Though, you don't have to explicitly design the service URI; it will be automatically inferred like: http://...[baseuri]/[operation]?argFoo={argFoo}&argBar={argBar}...

Adding [WebGet] or [WebInvoke] does not automatically work for WCF engine to assure that it is RESTful service. You also have to use either a dedicated channel factory or a service host, or WebHttpBehavior, a custom IEndpointBehavior. While WebHttpBehavior plays the primary role for everything I explained above and more, former approach would be easier.

Now we are ready to write sample code and run.

// client
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Web;

[ServiceContract]
public interface IMyService
{
  [OperationContract]
  [WebGet (RequestFormat = WebMessageFormat.Json,
           ResponseFormat = WebMessageFormat.Json)]
  string Greet (string input);
}

public class Test
{
  public static void Main (string [] args)
  {
    string name = args.Length > 0 ? args [0] : "anonymous joe";
    string url = "http://localhost:8080";
    WebHttpBinding b = new WebHttpBinding ();
    var f = new WebChannelFactory (b, new Uri (url));
    IMyService s = f.CreateChannel ();
    Console.WriteLine (s.Greet (name));
  }
}

// service
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;

[ServiceContract]
public interface IMyService
{
  [OperationContract]
  [WebGet (RequestFormat = WebMessageFormat.Json,
           ResponseFormat = WebMessageFormat.Json)]
  string Greet (string input);
}

public class MyService : IMyService
{
  public string Greet (string input)
  {
    return "hola, " + input;
  }
}

public class Test
{
  public static void Main ()
  {
    string url = "http://localhost:8080/";
    WebHttpBinding b = new WebHttpBinding ();
    var host = new WebServiceHost (typeof (MyService), new Uri (url));
    host.AddServiceEndpoint (typeof (IMyService), b, "");
    host.Open ();
    Console.WriteLine ("--- type [CR] to quit ---");
    Console.ReadLine ();
    host.Close ();
  }
}

In the example above, I used WebChannelFactory<T> instead of client class which is derived from ClientBase<T>, but as long as you use a gives instance of IMyService from it, its role is almost the same as ClientBase.

Limitations

Sadly WebHttpBinding is not available in Moonlight (nor Silverlight). It is not because the REST style does not fit Silverlight or RIA land in general, but because Silverlight API lacks behavioral extensibility API. I see there is no reason to not add things like IEndpointBehavior, but that's what Microsoft API does.

While WebHttpBinding is more WWW-friendly, it is not always possible to handle RESTful services. To handle JSON objects, it automatically adds type IDs during serialization and expects the IDs on deserialization.

Mono WCF Advent Day 8: MonoTouch

| 5 Comments | 1 TrackBack

WCF is also available in iPhone land. MonoTouch had started based on "2.1" profile of Mono, which is for Moonlight, and then extended its coverage to some .NET API such as System.Web.Services and ADO.NET. Since WCF is part of Moonlight, it was also possible to get included in MonoTouch.

Example

Here I'm sort of expecting you knowing basic how-to in MonoTouch land. I'm explaining the procedure below as what I did with MonoTouch on Snow Leopard and iPhone SDK 3.1:

  • Edit MainWindow.xib to add a button. -- Create "MonoTouch application project", say, monotouchwcf -- Double click MainWindow.xib to open Interface Builder. -- Select "Window" and add Round Rect Button and Label (drag them onto the Window for each). -- Select the button and give the button title like "Tap me". -- Go to library window, select "Classes" tab and select "AppDelegate" which is likely at the top of the list. Add "button" and "label" outlets. -- Go to IB's main window and select AppDelegate there, and connect its "button" outlet to the actual Round Rect Button on Window. Do the same for "label" output and the actual Label on the Window. Save the XIB and it's done in IB.
  • Create and add client proxy code -- Run service like we did for Moonlight. -- run svcutil to create client proxy like we did for moonlight. -- Add "output.cs" from svcutil result to the project.
  • Add service invocation via the client proxy in the application -- Add reference to System.ServiceModel.dll in the project. -- Add "using System.ServiceModel" at the top of the source. -- In Main.cs, add code lines below in FinishedLaunching() method, like below.
  • Run the app and touch the button.
        public override bool FinishedLaunching (UIApplication app, NSDictionary options)
        {
            // If you have defined a view, add it here:
            // window.AddSubview (navigationController.View);

            window.MakeKeyAndVisible ();

            this.button.TouchDown += delegate {
                var binding = new BasicHttpBinding ();
                var address = new EndpointAddress ("http://192.168.1.3:8080/test.svc");
                var client = new HelloServiceClient (binding, address);
                client.GreetCompleted += delegate(object sender, GreetCompletedEventArgs e) {
                    this.InvokeOnMainThread (delegate {
                        this.label.Text = e.Result;
                    });
                };
                this.label.Text = "sending request...";
                client.GreetAsync ("monotouch");
            };
            return true;
        }

Note that:

  • Do not use "localhost" as the remote endpoint address. It is not the iPhone itself but the development machine that runs corresponding WCF service. Use IP directly instead, for example.
  • Use InvokeOnMainThread() to update the application UI.

sshot-mt-wcf.png

Some notes on WCF in MonoTouch

The usage in MonoTouch almost the same as Moonlight does, while there is no "async-only" limitation in the runtime. You can use sync operation calls. Though I used event-based async model in the example above. (The future versions of svcutil might stop supporting sync calls due to moonlight proxy generator limitation. But sync client should work at runtime level anyways.)

On the other hand, there is a big limitation in MonoTouch in general - you cannot dynamically generate runnable code. And since WCF often involves dynamic code generation, you have to be caucious to not do it. For example, you have to override ClientBase<T>.CreateChannel() by using ClientBase<T>.ChannelBase<T> . If you don't do that, the default Channel property is generated by ChannelFactory<T>.CreateChannel() method, which dynamically creates an implementation of the contract interface using System.Reflection.Emit API.


Mono WCF Advent Day 7: Moonlight

| 1 Comment | No TrackBacks

Our WCF is ready in Moonlight 2.0. It provides Silverlight 2.0 compatibility, and filling 3.0 support should be almost easy (we need to implement things like CookieContainer support etc.). Actually our WCF stack has been improved a lot by the moonlight effort (the team gave several bug reports).

I haven't explained how to prepare Moonlight environment. Theis a wiki page for instructions I linked above (I assume it will be frequently updated, so I'm not going to give the anchor to the build instruction section now).

Also, when creating moonlight apps, make sure you have 'moonlight-web-devel' package (in case it is available in your distro) or some sort of equivalents (e.g. "MoonSDK") installed and set up.

Some notes

  • WCF in .NET and WCF in Moonlight are significantly different. We have almost feature complete WCF in Moonlight, while we lack a lot of features in .NET WCF. In WCF only BasicHttpBinding is supported in System.ServiceModel.dll (as Silverlight does).
  • As I mentioned on Day 5, Moonlight is the land of "async everywhere". So there isn't sync service calls. Operation methods are called with BeginFoo() and EndFoo() instead of Foo(). If you use them, EndFoo() is expected to be called inside AsyncCallback of the BeginFoo() argument. FooAsync() pattern is easier.
  • Silverlight applications may contain Microsoft's SDK assemblies. They are not what we implemented. For example, System.Xml.Linq.dll, System.Json.dll and System.ServiceModel.PollingDuplex.dll are SDK assemblies. I have never tried the last assembly, so it may or may not work.
  • Silverlight client can access the services only when it is from allowed domain by the website. "clientaccesspolicy.xml" and "crossdomain.xml" play the key role on the permission (I don't explain their details here, MSDN would give you hints).

Example

Now, let's build a simple app and see it's working. (NOTE: Here I use the latest version of svcutil which includes support for event-based async call I explained on Day 5.)

While it is possible to create a Moonlight application from console using "smcs" and "mxap" tools, I take the easiest way to create Moonlight project.

  • Start MonoDevelop (2.2 would be the best; earlier versions that support Moonlight project is fine).
  • Create a "Moonlight Application project", say, moonlight1. There are App.xaml, App.xaml.cs (folded), Page.xaml and Page.xaml.cs (folded) as the sources.
  • Just build and run to make sure it works (a web page with moonlight xap, printing "Hello Moon").
  • Prepare a WCF service which is hosted under xsp2 as explained on Day 6. It is important to host a service on ASP.NET since you have to provide access to "clientaccesspolicy.xml" (explained above). You cannot do it with a standalone ServiceHost at least in simple manner.
  • Create "clientaccesspolicy.xml" file on the hosted root directory. Moonlight WCF client will try to httpget the file. An example file content is pasted below, which states that it accepts SOAP requests from anywhere.
  • Create a client proxy explained on Day 5, by running: svcutil -moonlight http://localhost:8080/test.svc/wsdl
  • Open Page.xaml.cs on MonoDevelop, and add the code below for service invocation. The example means that the result from the SOAP response will be shown as a Javascript alert message when the application is loaded.
  • Run the Moonlight application project (F5 or F8). Moonlight addin will open your browser and show the test page.
<!-- clientaccesspolicy.xml -->
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-methods="*" http-request-headers="*">
        <domain uri="*">
        </domain>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true">
        </resource>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>
// Page.xaml.cs
this.Loaded += delegate {
  var binding = new BasicHttpBinding ();
  var address = new EndpointAddress ("http://localhost:8080/test.svc");
  var client = new HelloServiceClient (binding, address);
  client.GreetCompleted += delegate (object o, GreetCompletedEventArgs e) {
    System.Windows.Browser.HtmlPage.Window.Dispatcher.BeginInvoke (delegate {
        System.Windows.Browser.HtmlPage.Window.Alert (e.Result);
        tb.Text = e.Result;
    });
  };
  client.GreetAsync ("moonlight");
};

moonlight2-wcf-md.png

Find recent content on the main index or look in the archives to find all content.

Recent Comments

  • Rasteiro: Hi! Does BasicHttpBinding in MonoTouch takes care of Http Cookies read more
  • Atsushi Eno: -monotouch options is available only in post-2.6.x mono (i.e. git read more
  • Rick: Not sure if this is being monitored, but I am read more
  • chiyogami: nice articles about Moonlight 2.0. It provides Silverlight 2.0 compatibility, read more
  • Atsushi Eno: It is (of course) in svcutil.exe. If you're asking about read more
  • JSpoon: Hi, I want to ask you, where can I find read more
  • Atsushi Eno: Yes, I had a look at REST starter kit earlier read more
  • gathor: That's really cool, seriously. Have you considered support the WCF read more
  • Atsushi Eno: From your entire comment, I could only guess you are read more
  • Atsushi Eno: REST service on trunk is very unstable these days. Anyways read more

Recent Assets

  • mldsp-gtk-2009xmas.png
  • sshot-mt-wcf.png
  • moonlight2-wcf-md.png
  • mldsp.png
  • linqdatasource.png

Categories

Pages

Powered by Movable Type 4.21-en