wiki:Future/RESTfulWebServices

Version 1 (modified by jbirch, 17 years ago) ( diff )

Moving the page...

Overview

This page is a living proposal for defining a RESTful Web Service interface to MapGuide. Most of the discussion here will center around the concepts of Resources, Addressability, and Representations (and probably some side debate about state). If you have no idea what I am talking about the O'Reilly book RESTful Web Services by Leonard Richardson and Sam Ruby is a great place to start. RESTful web services are all about using HTTP the way it was meant to be used, and as such most operations are performed using standard HTTP GET, PUT, and DELETE methods. The HTTP HEAD, OPTIONS, and POST methods also play a role, but POST in particular needs to be used carefully in order to be considered RESTful.

For now lets assume the URI: http://somemgsite.org/mapguide/rest is the root of the MapGuide RESTful web services. All subsequent URIs below will leave this part off and focus on resources and their representations.

Resources and Representations

In my twisted little mind MapGuide at its core is a storage container of Resources (or more correctly the Resource Database is the container and Resource Service is the API for accessing it). The other MapGuide Services are a means of providing alternate representations of the resources stored in the resource database. In fact it is fairly easy to think of each Resource Type as having a number of representations, e.g. a Feature Source Definition has it's content (the resource itself), a schema, and features. If you follow and buy into that line of thinking, then the RESTful interface falls out fairly naturally. Let's start by looking at Resources themselves and the operations of Resource Service in a RESTful kind of way. Then we'll look at the different representations of each resource type and how that maps to our RESTful way of thinking.

Resources and the Resource Service

Resource Service supports the manipulation of resource content, headers, and data. A RESTful interface to get a resource might be:

GET
.../library/MyStuff/Parcels.FeatureSource/content.xml

In this case the XML definition of the resource comes back in the HTTP response envelope. To get the JSON representation one could use:

GET
.../library/MyStuff/Parcels.FeatureSource/content.json

To change or create a resource the PUT operation would be used instead with the resource content in the HTTP request envelope. For example:

PUT
.../library/MyStuff/Parcels.FeatureSource/content.xml

Likewise to get or set a resource header to update permissions or meta data would be as follows:

GET
.../library/MyStuff/Parcels.FeatureSource/header.xml

PUT
.../library/MyStuff/Parcels.FeatureSource/header.xml

And similarly to modify resource data the URIs would be:

GET
.../library/MyStuff/Parcels.FeatureSource/data/<dataname>.<datatype>

PUT
.../library/MyStuff/Parcels.FeatureSource/data/<dataname>.<datatype>

Where <dataname> is the name of the data element and <datatype> is one of file, string, or stream. The actual type of the data element being stored should be specified in the media-type in the HTTP header.

Another typical operation in Resource Service is enumerating resources and resource data. At first that seems simple, just use an HTTP GET operation on a library folder. However that could easily flood the client with way too much information. The EnumerateResources operation in Resource Service takes two parameters, depth and resource type, to limit the enumeration. We could support these through perverse URI encoding, but I think this is a better place to introduce parameters. So I propose the following:

GET
.../library/MyStuff.xml

Optional Parameters:
   depth=<depth>
   type=<resourcetype>

Like the other operations above we could also support /json. The depth and type parameters are optional and match those of the existing EnumerateResources operation. Enumerating resource data is easier and would be represented as follows:

GET
.../library/MyStuff/Parcels.FeatureSource/data.xml

So what about those session-based resources that MapGuide is so dependent on. Well, from my perspective that is easy. You simply change the URI to address the session. For example to get layer definition content from a session the following could be used:

GET
.../session/<uglysessionid>/Parcels.LayerDefinition/content.xml

TBD - Still need to think about how to support other Resource Service operations like Move, Copy, ApplyPackage, etc.

Alternate Representations for Feature Sources

Now this is where things start to get interesting. What alternate representations do we expose for Feature Sources to support the Feature Services API. We'll start with basic schema access and drill down into feature selection, update, etc. So to describe the schema of a Feature Source I would propose something like:

GET
.../library/MyStuff/MyData.FeatureSource/schema.xml [or .json]

Want just the schema of a particular schema or even a single class? How about:

GET
.../library/MyStuff/MyData.FeatureSource/schema/<schemaname>.xml [or .json]
GET
.../library/MyStuff/MyData.FeatureSource/schema/<schemaname>/<classname>.xml [or .json]

To get a list of the Spatial Contexts the following might work:

GET
.../library/MyStuff/MyData.FeatureSource/spatialcontexts.xml [or .json]

Retrieving all features of a given class might look something like:

GET
.../library/MyStuff/MyData.FeatureSource/features/<schemaname>/<classname>.xml [or .json]

Optional Parameters:
   properties=<p1,p2,...,pn>
   maxFeatures=n 
   filter=<fdo filter>
   spatialFilter=<fdo spatial filter>
   orderBy=<p1,p2,...,pn>
   orderOption=<ascending | descending>

For feature classes that have a single identity property, the following syntax may be used to retrieve a single feature:

GET
.../library/MyStuff/MyData.FeatureSource/features/<schemaname>/<classname>/<id>.xml [or .json]

Where <id> is the unique id of the feature. Deleting features can be performed with the HTTP DELETE method as follows:

DELETE
.../library/MyStuff/MyData.FeatureSource/features/<schemaname>/<classname>/<id>

or

DELETE
.../library/MyStuff/MyData.FeatureSource/features/<schemaname>/<classname>

Required Parameter:
   filter=<fdo filter>

To support Insert and Update operations we'll need to rely on the HTTP POST (insert) and PUT (update) methods. These operations can be defined as follows:

POST
.../library/MyStuff/MyData.FeatureSource/features/<schemaname>/<classname>

and

PUT
.../library/MyStuff/MyData.FeatureSource/features/<schemaname>/<classname>

Required Parameter:
   filter="<fdo filter>"

In both cases the HTTP request envelope must contain a property value collection that defines either the properties of the newly created feature or the properties to be updated and their new values. The collection could be in either JSON or XML format and an open question I have is whether the URI should specify the expected type?

TBD - Still need to think about how to support other Feature Service operations


Alternate Representations for Maps, Map Definitions, and Layer Definitions

I decided to lump the representations for Maps, Map Definitions, and Layer Definitions together as they all relate to creating and interacting with maps. The first thing that needs to be understood here is the relationship between a Map Definition (resource type MapDefinition) and a Map (resource type Map and object representation MgMap). These are not the same things, however they are closely related. Looking at it from a programming perspective, a Map Definition would be a class and a Map would be an instance of a Map Definition. In more human terms, a Map Definition is a template or set of rules and a map is a per client/user object built from a Map Definition template. The Map object is stored in session state and knows the current list of layers and their visibility, current scale, and current extents. Having this information server-side allows MapGuide to render and plot maps with minimal client-server interaction.
The first thing we need is the ability to create a Map resource / MgMap object.

POST
.../library/MyStuff/MyMap.MapDefinition/createmap

Optional Parameters:
   name=<mapname>

POST
.../session/<uglysesssionid>/createmap

Required Parameters:
   name=<mapname>
   srs=<srswkt>
   envelope=x1,y1,x2,y2

The first creates a new map from the Map Definition resource identified in the URI. The later creates an empty map with the specified name, coordinate system, and extent. Now that we have a map, we need some way to render the map (with and without selection), the dynamic overlay (with and without selection), and base map tiles.

GET
.../session/<uglysesssionid>/MyMap.Map/image.png [or .jpg or .gif]

Optional Parameters:
   <Big list of them from RenderingService::RenderMap>

POST
.../session/<uglysesssionid>/MyMap.Map/image.png [or .jpg or .gif]

Optional Parameters:
   <Big list of them from RenderingService::RenderMap>

The first one would render a single image of the map (all visible layers including base map) using the current state of the map from the session. The second provides a way to update the map state stored in the session and return a new image in a single shot. In the HTTP POST method case the post data is expected to contain a list of commands that would alter the map state, e.g. turn on these layers, turn off those layers, set the new center to this, etc.

So what about support for tiled maps with dynamic overlays.

GET
.../session/<uglysesssionid>/MyMap.Map/overlayimage.png [or .jpg or .gif]

Optional Parameters:
   <List of them from RenderingService::RenderDynamicOverlay>

POST
.../session/<uglysesssionid>/MyMap.Map/overlayimage.png [or .jpg or .gif]

Optional Parameters:
   <List of them from RenderingService::RenderDynamicOverlay>

GET
.../library/MyStuff/MyMap.MapDefinition/basetileimage/<baselayergroupname>/<scaleindex>/<tilecolumn>,<tilerow>

As with the normal map image delivery, the HTTP POST form of /overlayimage can include a set of commands for altering the state of the map object prior to rendering and returning the image. Note that base tile image access works based on a the Map Definition, not the runtime Map. That is because MapGuide caches base map tiles based on the groups within a Map Definition, not on a per user basis.

To build a cool client application, we need information about the map, the layers, the layer groups, etc. The next set of operations is designed to return an informational representation of map that can be used to create a compelling user interface.

GET
.../session/<uglysesssionid>/MyMap.Map.xml [or .json]

GET
.../session/<uglysesssionid>/MyMap.Map/layers.xml [or .json]

GET
.../session/<uglysesssionid>/MyMap.Map/layers/<layername>.xml [or .json]

GET
.../session/<uglysesssionid>/MyMap.Map/layergroups.xml [or .json]

GET
.../session/<uglysesssionid>/MyMap.Map/layergroups/<layergroupname>.xml [or .json]

The output of these needs some thought, but basically would encapsulate the properties found in the MgMap, MgLayer, and MgLayerGroup objects in the Web API. To complete this we need to include some way to generate a icons for the legend.

GET
.../library/MyStuff/MyLayer.LayerDefinition/legendicon/<mapscale>/<geomtype>/<themecat>.png [or .jpg or .gif]

Optional Parameters:
   width=<desiredimagewidth>
   height=<desiredimageheight>

Now of course no mapping client is complete without some way to interact with and query the features displayed on the map. The MapGuide Web API includes the !MgRenderingService::QueryFeatures APIs for this and I propose the following RESTful representation:

GET
.../session/<uglysesssionid>/MyMap.Map/features.xml [or .json]

Optional (but highly recommended) Parameters:
   layerNames=<l1, l2, ..., ln>
   filter=<fdo filter>
   spatialFilter=<fdo spatial filter>
   maxFeatures=<maxdesiredfeatures>

More parameters are probably needed here, but you get the idea.

TBD - Still need more stuff for adding layers dynamically, plotting to DWF, etc.


Note: Within all of this the handling of selection is bugging me. We need to think about that some more and figure out what is the right way of handling selection RESTfully.

Other Necessary Stuff

We need some way for an application to create a session. By simply support a POST to /session as follows we can meet that requirement.

POST
.../session

There is no required HTTP request envelope and the return envelope would simply contain the newly created session URI, e.g. http://somemgsite.org/mapguide/rest/session/<newuglysessionid>/.


Security, Authentication, and Access Control

TBD

Note: See TracWiki for help on using the wiki.