RestCake is a C# library for creating REST services without WCF.
WCF has many pain points:
- Date formatting is horrible (DataContractJsonSerializer)
- Can't serialize anonymous types
- Too much configuration
- Can't handle cycles in object graphs
- No global error handling
- UriTemplate params can only be passed in as strings
WCF is designed to be host agnostic, whereas REST implementations are very simple, and (for practical purposes) always use HTTP.
I love this quote (
source):
"I'm not sure I want to build on a layer [WCF REST] designed to factor HTTP in on top of a layer that was designed to factor it out [WCF]."
All in all, WCF is overkill for REST servcies that are not going to have any other endpoints, and will not be hosted outside of IIS.
All of its noble idealisms and abstractions get in the way.
Let me elaborate a bit more about how WCF gets in the way.
"\/Date(1280555181054-0700)\/". Ouch! If you're using the MS Ajax runtime, sure they fix this up for you,
but it's not very friendly. I've seen everything from hacks on the json2.js file to automatically fix these up to quick javascript
utility methods to convert to/from this awful date format. The worst part is you don't even have a choice. RestCake uses
the
Json.NET serializer, which lets you choose how your dates will be formatted.
The default format (ISO date format) can be parsed from its string representation directly to a javascript date, and a native javascript date
can be passed to the service and deserialized into a DateTime object. More than I can say for the MS AJAX format...
Sometimes you want your service method to return a simple pair or triple of objects. With anonymous types, it's very easy to creates
these kinds of tuples:
var result = new
{
Person = somePerson,
Status = Status.Success,
NumProjects = projects.Count()
};
The DataContractJsonSerializer can't serialize anonymous types, so you'd have to create a class that represents each result type that you
want your service methods to return. Json.NET, on the other hand, will serialize the above object just fine.
While they have cut down the amount of configuration for WCF significantly with recent releases, there is still a lot to do. The
<system.serviceModel> section in your web.config can get pretty large sometimes. RestCake doesn't require any special settings
in your web.config. The REST services are based on a very simple base class,
RestHttpHandler that implements
the
System.Web.IHttpHandler interface. Calls to your service simply go to the
ProcessRequest() method
of that interface, and it's all based on standard HTTP. While REST is a "high level concept", in practice it's nearly always implemented using
the HTTP protocol, and a basic IHttpHandler is more than enough to get a flexible service going.
RestCake requires no configuration in your web.config. At some point there will probably be an option to specify certain global settings,
but it's not required.
What's a cycle? An example would be if you had a Parent object with a List<Child> named Children. Each Child object
in that list has a Parent property that refers back to the Parent. Parent.Children[0].Parent.Children[0].Parent.Children[0]....we're
going in circles. That's a cycle, and because json can't represent references in an object literal, the DataContractJsonSerializer
throws an exception when it encounters a cycle in your object graph. While that's very noble and idealistic, it's not very practical.
Json.NET gives you many different options on how to deal with cycles, the easiest being to simply ignore them. In the example case,
the Parent object would have a list of child objects, but those child objects would not have a reference back to the parent. For
most people's usage scenarios (getting DTO objects to your client script to update the web UI), that's just fine.
There may be someway to easily handle all errors in your WCF service methods, but I certainly haven't found it. I'm really tired of
creating FaultException<ArgumentException> objects. Really? An exception type that takes another exception type
as a generic type parameter? Oh, and when you create that, you end up having to repeat all your string messages twice. Very fun. You
can forget about
Application_Error() in your Global.asax too. Doesn't fire when an error occurs in your WCF
service method. But guess what! It
does fire when an error occurs in an IHttpHandler! The
RestHttpHandler in RestCake is based off of the simple
IHttpHandler interface, and handling
uncaught exceptions is a breeze. Once again, WCF's noble host agnosticism has made something that should be easy, quite difficult.
Any variables in your UriTemplate that are part of the Uri and not the query string, have to have matching parameters in your service
method, and the type
has to be string. It's can't be any of the other primitive data types. So
in your service methods, you end up doing a lot of validation and TryParsing, which is not fun. It gets in the way and creates noise.
By not "sane" I mean you don't get what you want 99% of the time. A bit of code is the easiest way to explain this one.
This outputs:
[{"Key":"Fname","Value":"Sam"},{"Key":"Lname","Value":"Meacham"}].
What? Oh, it's a collection of (key,value) pairs. How noble! Yes, it will work if I use something totally
unreasonable as a key value, such as "#$%^&", but that would be stupid, and I wouldn't do it.
Let's take a look at how Json.NET serializes that dictionary:
In addition to the actual serialization being much, much easier, the result is sane!
{""Fname":"Sam","Lname":"Meacham"}. Just what I wanted. Now, if for some reason I
need the other behavior, a list of (key,value) pairs, it's easy enough:
So we get what we want 99% of the time with the default behavior, and write 1 extra line of effort when we want the 1% of
the time behavior. Where with WCF, you get what you
don't want 99% of the time, and since it can't
serialize anything sane, you have to write a bunch of javascript code to "fix up" the dictionaries you get back from your
services. And it's a LOT of extra effort. Especially when you have to "reverse fix up" the values before
you send them back to your services. Ouch.
This is a strange one, and a bit trivial, but it's very annoying. I like immutable types. Not having to deal
with state change is a good thing, especially when you want to start doing a lot of parallel processing. Immutable
types are immune to side effects. They don't require having locks taken out on them. Et cetera et
cetera. Consider this simple immutable type:
Trying to serialize or deserialize an object of type Person with the DataContractJsonSerializer will result
in a runtime exception, because the [DataMember] properties don't have setters. While it does work if
you have private setters, your type isn't really immutable then, is it? You can't have automatic properties
that are based on readonly backing fields. The solution is to put the [DataMember] attributes on your backing
fields (m_id and m_name in this case), but then you have to do this:
Very annoying. And not refactor safe. Once again, Json.NET has none of these problems. In fact,
your types don't need any special attributes at all.
Let's take a look at a very basic WCF REST service.
Now, to get JSON, you'd either have to specify the WebMessageFormat in the WebGet attribute,
or specify that you want the default to be JSON in your web.config, somewhere in the <system.serviceModel>
section. That's not a bad thing, it's just something you have to remember to do, or you'll get XML back (hint: I'm
not a huge fan of XML).
To access that service, you've got quite a few options. You can create a .svc file that specifies what host factory
you want to use, and then navigate to that .svc file in your browser. Or, you can ditch the .svc file, and set up some
ServiceRoutes in your Global.asax file, using the WebServiceHostFactory. That last option is the best, because you get clean urls.
Let's see how we'd create this same service with
RestCake.
Do you see the difference? It's subtle! This HelloWorldService class inherits from
RestHttpHandler,
and
that is the only difference. You don't even need a .ashx file for your IHttpHandler, though you can
certainly use one if you want. If you have a .ashx file, you can just navigate right to it to use your service. If you don't,
you can set up a
GenericHandlerRoute (part of RestCake) in your Global.asax, like this:
RestCake uses the existing WCF attributes to figure everything out. It's based on the same
UriTemplate
processing. When this service is called for the first time, The
RestHttpHandler base class will collect
some metadata on all of the service methods in the service class. ProcessRequest() will figure out which method needs to
be called, and call it with the parameter values that came with your service call (from the UriTemplate, the query string,
or the POST, PUT, or DELETE data, either in query string or json format).
Also, with the above RestCake service, we don't need any special settings in our web.config. You navigate to the ashx file,
or the url of the route you set up, and it
just works.
- You have control over date formatting.
- Your service methods can return anonymous types.
- On the whole, your services execute more quickly (Json.NET is faster than the DCJS).
- You don't have any configuration to worry about.
- You can return object graphs that have cycles without getting runtime errors (this is great for you Entity Framework guys...).
- Your Application_Error method will fire for any unhandled exceptions.
- Dictionaries are serialized that way you want 99% of the time.
- Your service methods can take input UriTemplate parameters of any type: strings, primitive types, user class types, etc.
Using the
JsProxyCreator, you can either create a static javascript file from your assembly that has your services,
or generate the javascript files on the fly by navigating to your service special url. The javascript file is a
jquery proxy.
It's based on the
WCF proxy by Rick Strahl, but takes it quite a bit
further. Javascript proxy classes are created to mirror each of your service classes, and you have all of your service methods
ready to be called. Here's what calling that HelloWorld service would look like in client script.
Very easy. Include the proxy script, create a new instance of the service proxy, providing the base url to the service,
and call your method. In Visual Studio, you'll even get Intellisense for the method names, and the names of the arguments.
You don't have to do any JSON.stringifying or parsing yourself, it's all wrapped up in the javascript proxy classes.
- Download RestCake
- Add the reference to your existing WCF REST services project
- Open up one of your [ServiceContract] classes, add the using RestCake directive, and change to class to inherit from RestHttpHandler
- Go to your Global.asax Application_Start(), and create a new GenericHandlerRoute<T> route with a unique url ("services/myservice")
- In a web page, add a <script> tag with the source pointing to "yoursite/services/myservice/_js?jquery&base=true" to include the automatic javascript proxy class
- Create a new service proxy: var g_svc = new MyNamespace.MyServiceProxy("/services/myservice/");
- Call your service methods from the javascript proxy object: g_svc.SayHello(arg1, function(){ /* success handler */ });
Once you're convinced, you can get rid of a bunch of WCF stuff in your web.config, cleaning things up a bit. You can change your service
methods to have correctly typed input parameters. No more TryParsing all the strings you were passing in before. You can get rid of all
the javascript code that "fixed up" the results of crappy, inflexible serialization. You can get rid of classes that you created just to
create tuples of objects you wanted to return together. You can get rid of whatever crazy error handling you had to handle exceptions in
your service methods. You can forget about faults altogether. Rest is fun again. As fun at eating cake.