Changes between Initial Version and Version 1 of Future/RESTfulWebServices


Ignore:
Timestamp:
Mar 18, 2008, 9:49:10 PM (16 years ago)
Author:
jbirch
Comment:

Moving the page...

Legend:

Unmodified
Added
Removed
Modified
  • Future/RESTfulWebServices

    v1 v1  
     1[[PageOutline]]
     2= Overview =
     3This 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.
     4
     5For 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.
     6
     7= Resources and Representations =
     8In 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.
     9
     10== Resources and the Resource Service ==
     11
     12Resource Service supports the manipulation of resource content, headers, and data. A RESTful interface to get a resource might be:
     13{{{
     14GET
     15.../library/MyStuff/Parcels.FeatureSource/content.xml
     16}}}
     17In this case the XML definition of the resource comes back in the HTTP response envelope. To get the JSON representation one could use:
     18{{{
     19GET
     20.../library/MyStuff/Parcels.FeatureSource/content.json
     21}}}
     22To change or create a resource the PUT operation would be used instead with the resource content in the HTTP request envelope. For example:
     23{{{
     24PUT
     25.../library/MyStuff/Parcels.FeatureSource/content.xml
     26}}}
     27Likewise to get or set a resource header to update permissions or meta data would be as follows:
     28{{{
     29GET
     30.../library/MyStuff/Parcels.FeatureSource/header.xml
     31
     32PUT
     33.../library/MyStuff/Parcels.FeatureSource/header.xml
     34}}}
     35And similarly to modify resource data the URIs would be:
     36{{{
     37GET
     38.../library/MyStuff/Parcels.FeatureSource/data/<dataname>.<datatype>
     39
     40PUT
     41.../library/MyStuff/Parcels.FeatureSource/data/<dataname>.<datatype>
     42}}}
     43Where <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.
     44[[br]][[br]]
     45Another 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:
     46{{{
     47GET
     48.../library/MyStuff.xml
     49
     50Optional Parameters:
     51   depth=<depth>
     52   type=<resourcetype>
     53}}}
     54Like 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:
     55{{{
     56GET
     57.../library/MyStuff/Parcels.FeatureSource/data.xml
     58}}}
     59So 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:
     60{{{
     61GET
     62.../session/<uglysessionid>/Parcels.LayerDefinition/content.xml
     63}}}
     64'''TBD''' - Still need to think about how to support other Resource Service operations like Move, Copy, ApplyPackage, etc.
     65[[BR]]
     66[[BR]]
     67
     68== Alternate Representations for Feature Sources ==
     69Now 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:
     70{{{
     71GET
     72.../library/MyStuff/MyData.FeatureSource/schema.xml [or .json]
     73}}}
     74Want just the schema of a particular schema or even a single class? How about:
     75{{{
     76GET
     77.../library/MyStuff/MyData.FeatureSource/schema/<schemaname>.xml [or .json]
     78GET
     79.../library/MyStuff/MyData.FeatureSource/schema/<schemaname>/<classname>.xml [or .json]
     80}}}
     81To get a list of the Spatial Contexts the following might work:
     82{{{
     83GET
     84.../library/MyStuff/MyData.FeatureSource/spatialcontexts.xml [or .json]
     85}}}
     86Retrieving all features of a given class might look something like:
     87{{{
     88GET
     89.../library/MyStuff/MyData.FeatureSource/features/<schemaname>/<classname>.xml [or .json]
     90
     91Optional Parameters:
     92   properties=<p1,p2,...,pn>
     93   maxFeatures=n
     94   filter=<fdo filter>
     95   spatialFilter=<fdo spatial filter>
     96   orderBy=<p1,p2,...,pn>
     97   orderOption=<ascending | descending>
     98}}}
     99For feature classes that have a single identity property, the following syntax may be used to retrieve a single feature:
     100{{{
     101GET
     102.../library/MyStuff/MyData.FeatureSource/features/<schemaname>/<classname>/<id>.xml [or .json]
     103}}}
     104Where <id> is the unique id of the feature. Deleting features can be performed with the HTTP DELETE method as follows:
     105{{{
     106DELETE
     107.../library/MyStuff/MyData.FeatureSource/features/<schemaname>/<classname>/<id>
     108
     109or
     110
     111DELETE
     112.../library/MyStuff/MyData.FeatureSource/features/<schemaname>/<classname>
     113
     114Required Parameter:
     115   filter=<fdo filter>
     116}}}
     117To 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:
     118{{{
     119POST
     120.../library/MyStuff/MyData.FeatureSource/features/<schemaname>/<classname>
     121
     122and
     123
     124PUT
     125.../library/MyStuff/MyData.FeatureSource/features/<schemaname>/<classname>
     126
     127Required Parameter:
     128   filter="<fdo filter>"
     129}}}
     130In 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?
     131[[BR]]
     132[[BR]]
     133'''TBD''' - Still need to think about how to support other Feature Service operations
     134[[BR]]
     135[[BR]]
     136[[BR]]
     137== Alternate Representations for Maps, Map Definitions, and Layer Definitions ==
     138I 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.
     139[[BR]]
     140The first thing we need is the ability to create a Map resource / !MgMap object.
     141{{{
     142POST
     143.../library/MyStuff/MyMap.MapDefinition/createmap
     144
     145Optional Parameters:
     146   name=<mapname>
     147
     148POST
     149.../session/<uglysesssionid>/createmap
     150
     151Required Parameters:
     152   name=<mapname>
     153   srs=<srswkt>
     154   envelope=x1,y1,x2,y2
     155}}}
     156The 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.
     157{{{
     158GET
     159.../session/<uglysesssionid>/MyMap.Map/image.png [or .jpg or .gif]
     160
     161Optional Parameters:
     162   <Big list of them from RenderingService::RenderMap>
     163
     164POST
     165.../session/<uglysesssionid>/MyMap.Map/image.png [or .jpg or .gif]
     166
     167Optional Parameters:
     168   <Big list of them from RenderingService::RenderMap>
     169}}}
     170The 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.
     171[[BR]]
     172[[BR]]
     173So what about support for tiled maps with dynamic overlays.
     174{{{
     175GET
     176.../session/<uglysesssionid>/MyMap.Map/overlayimage.png [or .jpg or .gif]
     177
     178Optional Parameters:
     179   <List of them from RenderingService::RenderDynamicOverlay>
     180
     181POST
     182.../session/<uglysesssionid>/MyMap.Map/overlayimage.png [or .jpg or .gif]
     183
     184Optional Parameters:
     185   <List of them from RenderingService::RenderDynamicOverlay>
     186
     187GET
     188.../library/MyStuff/MyMap.MapDefinition/basetileimage/<baselayergroupname>/<scaleindex>/<tilecolumn>,<tilerow>
     189
     190}}}
     191As 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.
     192[[BR]]
     193[[BR]]
     194To 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.
     195{{{
     196GET
     197.../session/<uglysesssionid>/MyMap.Map.xml [or .json]
     198
     199GET
     200.../session/<uglysesssionid>/MyMap.Map/layers.xml [or .json]
     201
     202GET
     203.../session/<uglysesssionid>/MyMap.Map/layers/<layername>.xml [or .json]
     204
     205GET
     206.../session/<uglysesssionid>/MyMap.Map/layergroups.xml [or .json]
     207
     208GET
     209.../session/<uglysesssionid>/MyMap.Map/layergroups/<layergroupname>.xml [or .json]
     210}}}
     211The 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.
     212{{{
     213GET
     214.../library/MyStuff/MyLayer.LayerDefinition/legendicon/<mapscale>/<geomtype>/<themecat>.png [or .jpg or .gif]
     215
     216Optional Parameters:
     217   width=<desiredimagewidth>
     218   height=<desiredimageheight>
     219}}}
     220Now 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:
     221{{{
     222GET
     223.../session/<uglysesssionid>/MyMap.Map/features.xml [or .json]
     224
     225Optional (but highly recommended) Parameters:
     226   layerNames=<l1, l2, ..., ln>
     227   filter=<fdo filter>
     228   spatialFilter=<fdo spatial filter>
     229   maxFeatures=<maxdesiredfeatures>
     230}}}
     231More parameters are probably needed here, but you get the idea.
     232[[BR]]
     233[[BR]]
     234'''TBD''' - Still need more stuff for adding layers dynamically, plotting to DWF, etc.
     235[[BR]]
     236[[BR]]
     237[[BR]]
     238'''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.
     239[[BR]]
     240== Other Necessary Stuff ==
     241We need some way for an application to create a session. By simply support a POST to /session as follows we can meet that requirement.
     242{{{
     243POST
     244.../session
     245}}}
     246There 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>/.
     247[[BR]]
     248[[BR]]
     249[[BR]]
     250= Security, Authentication, and Access Control =
     251TBD