ArcGIS Server REST SOEs with Complex Types

A week or so ago I had to write my first ArcGIS Server SOE in order to add some custom functionality into a legacy .NET web forms application, if you are not familiar with the concept of SOEs (Server Object Extensions) then I suggest you start by reading the Esri help as there is a fair amount of good documentation on the ArcGIS resource pages

All the examples were geared around using simple types as operation parameters which in some cases is fine but I decided to build mine using complex types. There are a few reasons that I prefer this approach.

  • If I need to add a parameter that is related to the type I can just add it as a property rather than having to edit the operation definition.
  • If I want to work with large amounts of data or collections of data then having to manually add these can be very laborious.
  • Having to manually parse the operation input to extract parameters values is also tedious.
  • I wanted to simplify the calling of the SOE from managed code and make the underlying mechanism as transparent to the user as possible.

Sample Implementation

For the purposes of this post I’ve created a sample solution that contains the basic structure for how I implemented this. The solution consists of

  • The SOE (which is a self registering console application – thanks to Kirk Kuykendall for the idea on this)
  • A common dll used to define the SOE model and names
  • A client dll used as an API to invoke the SOE operations
  • A client test project, in this case just a web page that calls the extension

Click here to download the source

Note that you will need the ArcGIS .NET SDK installed to build the solution.

Working with Complex Types as input parameters

There are standard conventions that must be followed to build a SOE so that it can be used with ArcGIS Server. The method definition for a SOE operation is

private byte[] SampleOperationHandler(NameValueCollection boundVariables,  
                                      JsonObject operationInput,  
                                      string outputFormat,  
                                      string requestProperties,  
                                  out string responseProperties)

the input parameter values are part of the operationInput and each one must be looked up by it’s name as defined in the operation schema. This schema is defined in the CreateRestSchema method and for the sample it looks like

private RestResource CreateRestSchema() 
{ 
    var soeResource = new RestResource(_soeName, false, RootResHandler); 

    // Add the operations 
    var operations = new List<RestOperation>(); 

    var sampleRestOperation = new RestOperation( 
                Common.Names.SampleOperationName, 
                new[] { typeof(GpsData).Name }, 
                new[] { "json" }, 
                SampleOperationHandler, 
                true); 
    operations.Add(sampleRestOperation); 

    // TODO : add more operations as needed 
    soeResource.operations = operations; 

    return soeResource; 
}

where GpsData is the name of the only parameter I’m defining. The type of this parameter isn’t defined so in theory I could pass anything but we need to know what it is when we parse the operationInput in order to extract the correct data. If we are using the Simple Type approach then it may be something like operationInput.TryGetAsBoolean… but here we are passing a Json object so we can use

JsonObject jsonObj;  
bool found = operationInput.TryGetJsonObject(typeof(GpsData).Name, out jsonObj); 
if (!found) 
    throw new ArgumentNullException(typeof(GpsData).Name); 
var gpsData = JsonableObject.FromJson<GpsData>(jsonObj.ToJson());

Now we have our deserialised object containing our data to work with in a few lines rather than the same code per property.  The conversion to and from Json is handled using the Json .NET library.

Invoking the Extension

Once the SOE is built and registered we need a way of calling it. The ClientTest project is a standard ASP.NET web site with one page that calls the sample operation on a button click.

protected void btnCallSampleOperation_Click(object sender, EventArgs e) 
{ 
    var gpsData = new GpsData 
    { 
        LayerName = "Land area", 
        NumberOfSatellites = 4, 
        FixAcquired = true, 
        Location = new Location 
        { 
            Longitude = 175.495605, 
            Latitude = -39.546977 
        } 
    }; 

   var result = OperationHelper.CallSampleOperation( 
     @"http://dave-pc/ArcGIS/rest/services/TestSoe/MapServer", 
     gpsData); 
}

Note that we only have to specify the url of the MapServer endpoint as the helper method constructs the full url for us. This provides a very clean and easy way to not only invoke the extension but also to ensure that we aren’t going to get runtime errors due to using magic strings.

public static OperationResult CallSampleOperation(  
                   string restEndpoint,  
                   GpsData gpsData,  
                   string proxyUrl) 
{ 
   var fullUrl = string.Format("{0}/exts/{1}/{2}", 
         restEndpoint.TrimEnd('/'), 
         Names.SoeName, 
         Names.SampleOperationName); 
 
   var parameters = new Dictionary<string, string> 
         { { typeof(GpsData).Name, gpsData.ToJson() } }; 
 
   var request = new Request(fullUrl, proxyUrl); 
 
   return request.SubmitRequest<OperationResult>(parameters); 
}

Checking the Data

The best way to find out how the SOE works is to run it for yourself. First you need to publish a map service with at least one layer and enable the extension.

soe in arccatalog

Once you have it setup you can browse the REST endpoint for your service and there will be an extra option showing the Extension

rest metadata 1

Click on that to view the supported operations. This page can be customised, here I have included the version number for the SOE.

rest

If you click on the SampleOperation you will see the following screen which can also be used to test the extension so long as you input the data correctly.

sampple operation

An easier way is to run the test page though and view the request using Fiddler. If you start the TestClient Default.aspx page and click on the button you should see the following

fiddler

This shows the Json as part of the message body with the result below. Note that you will have to change the values for the layer name and REST service url to match your environment.


Hopefully this gives you a starting point for working with REST SOEs and complex types in your applications. Any feedback is appreciated and thanks for reading.