ArcDeveloper REST: U R I and I R U

Sat, Mar 8, 2008 3-minute read
As I promised in my (first and) previous post, I am gonna quickly go over the very simple architecture of the ArcDeveloper REST API. This post covers how we expose our REST endpoint(s). At a high level, we wanted to expose a single REST endpoint that allowed the caller to specify the "service", "layer", and query parameters in order to get geographic features returned in a format, which is also specified by the caller. So, attempting to be as RESTy as possible, the URI looks like:
 http://<yourServer>/rest.svc/service/layer/3?format=geojson
It's clear and easy and other URI endpoints are easy to deduce. Yup, we are RESTing we the best of them. Our main endpoint is the rest.svc, which is WCF's way of exposing a service endpoint. The first question, then, we had to answer was how we would handle the URI routing. If the map services are registered dynamically (using Castle Windsor, which is arguably the greatest piece of code ever written. Ever. Not that I am prone to hyperbole. Oh, I'll cover the configuration in the next post) then we have to dissect the various pieces of the URI to:
  1. Get a hold of the service requested.
  2. Query the right layer
  3. Use the right query parameters.
  4. Grab the right formatter.
Luckily for us, .NET 3.5 had recently been released, incorporating some cool changes in WCF, the coolest one being the URITemplate (good background here). The URITemplate allows us to define URI templates (duh) and map them to service methods (or OperationContracts, in WCF-speak) complete with the mapping of parameters of the method. Let's see how we are using this now:

[OperationContract]

[WebGet(UriTemplate="{serviceName}/{featureType}/{featureId}?g={geom}",ResponseFormat=WebMessageFormat.Json)]

Stream GetSingleFeatureJSON(string serviceName, string featureType, string featureId, bool geom);

You can see how the dealies in the "{}" get mapped to the method parameters. Just to be complete, {serviceName} maps to the serviceName parameter, etc. The "geom" parameter specifies whether or not you want all the coordinates returned, because sometimes that can be a LOT of data. We also specify the response format here and the HTTP verb (WebGet== HTTP Get. I'll give you 3 guesses how you specify HTTP POST. Wrong. It's WebInvoke.) So, I know what you are saying. "WTF? You have hard-coded the format INTO THE BLOODY METHOD NAME! I am not reading any more of this tripe." Well, that's your prerogative (Tangent: I had NO idea that was how to spell that word. No wonder Bobby Brown is on drugs) but know that I am adhering to the Last Responsible Minute (LRM) doctrine. We only need (geo)JSON right now, meaning, no one has written any other formatters. Once we have another format, we'll put in a "format" parameter, remove the ResponseFormat and life will be all good. Weak excuse, you say? Maybe. Let's, uh, move on. The URIs the project currently supports are:
  http://yourserver/rest.svc/services
Will return information about all of the services that are available to be queried.
  http://yourserver/rest.svc/serviceName/layerName/OBJECTID?g=true
Will return the feature corresponding to the OBJECTID value from the layer specified by "layerName" in the service configured as "serviceName".
  http://yourserver/rest.svc/serviceName/layerName?query=whereClause&bbox=xmin,ymin,xmax,ymax&g=true
Will query layer named "layerName" in service configured as "serviceName" using the value in "whereClause" (like height>100 or streetname='Main' -- don't forget to escape your querystrings....) inside the bounding box specified by xmin, ymin and xmax, ymax. As always, g=true returns the full coordinates of each geometry. Also, the serviceName corresponds to the name given in the Windsor configuration, NOT the name of the ArcGIS Server service. In the next post (hopefully) I'll cover the service and its configuration, showing you how we leverage the total kick-assness of Windsor to make life easy.