JavaScript REST examples

The GeoServer REST API allows to perform CRUD operations against the GeoServer catalog via HTTP calls. It is possible to manage the resources representation using different formats: plain HTML (very limited and not useful to perform real CRUD operations), XML and JSON. The latter in particular are very flexible and useful especially when writing GUIs allowing users to perform quick, but still complex, remote operations on GeoServer catalog.

JSON and HTTP protocol via AJAX in particular are very interesting when trying to build up HTML pages using simple JavaScript to manage GeoServer resources via REST.

In this section we will see a nice example of JSON parsing/mapping of the GeoServer resources for reading their attributes, other than AJAX functions used to perform read/write operations allowing a user to update layers defaultStyle with a simple click on the GUI.

JSON/AJAX REST example in JavaScript

  1. Open up a new browser window and go to the URL:

    http://localhost:8083/geoserver/www/rest-example.html
    

    You should be able to see a very simple HTML page like the one depicted here below:

    ../_images/rest_js1.png

    Simple HTML page using JavaScript, JSON and AJAX to retrieve GeoServer styles and layers.

    As you may notice, if everything goes well, at startup there are two select boxes which are dynamically populated with the list of local GeoServer available styles and layers.

  2. Select the style line from the Available Styles select box and bstreets from the Available Layers select box

    ../_images/rest_js2.png

    You should be able the selected names appear on the two text boxes in the bottom near the Assign SLD —> Layer button.

  3. Click on the button

    Assign SLD ---> Layer
    

    and confirm all the questions. You should get a Done! message at the end of the process.

    ../_images/rest_js3.png
  4. Go to the Map Preview page and select the bstreets OpenLayers preview

    ../_images/rest_js4.png

    notice that the default style of Boulder Streets layer is changed to plain blue lines.

    ../_images/rest_js5.png
  5. Now go back to the rest js example and repeat the operations above to set the bstreets style to streets

    ../_images/rest_js6.png

    and confirm from the Map Preview that the default style has been changed back to the original one.

    Note

    With the streets style you need to zoom in in order to see the boulder streets coming up due to the scale denominator.

    ../_images/rest_js7.png

Taking a look at the concepts and code

First of all, notice that the example has been made by using two files

$geoserver_data/www/rest-example.html
$geoserver_data/www/rest-example.js

The /www directory under the $geoserver_data (%geoserver_data% in Windows) is a special directory. The GeoServer filters allow to stream out directly the content of this directory as if it was published on a web server.

We placed our files here since we used direct AJAX calls to GeoServer REST API, and as you may already know, in order to do this usually you must be in the same context due to the Same-Origin policy of the browsers. Of course there are ways to allow AJAX calls from other origins/domains but is not of specific interest for this topic.

  1. Open with a simple text editor the file rest-example.html. You will find a very simple HTML below

    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
            <title>GeoServer Workshop</title>
    
            <script src="webtoolkit.base64.js"></script>
            <script src="rest-example.js"></script>
    
            <script language="Javascript">
               var geoserverUsername='admin';
               var geoserverPassword='***********';
            </script>
      </head>
    
      <body onload="init()">
            <h1 id="title">GeoServer Workshop - REST API trough JavaScript</h1>
    
            <h2>Available Styles</h2>
               <select id="sldSelect" onchange="updateSelectedSld(this)"></select>
    
            <h2>Available Layers: 'geosolutions' workspace</h2>
               <select id="lyrSelect" onchange="updateSelectedLyr(this)"></select>
    
            <br/><br/>
            SLD: <input type="text" id="selectedSld" style="witdh: 300px" readonly> -
            Layer: <input type="text" id="selectedLyr" style="witdh: 300px" readonly>
            <button id="assignSld" onclick="assignSldToLyr()">Assign SLD ----> Layer</button>
      </body>
    
    </html>
    

    Take a look first to the head section. We load two JavaScript sources and define two global variables geoserverUsername and geoserverPassword which must be valued with the correct credentials of a user having access to GeoServer REST.

    Warning

    In common practice never place plain username and password on an HTML page. This is only for the purposes of this very simple exercise. You can use a simple form in order to provide credentials instead. Still be sure to pass the credentianls through a secure channel.

    The first file

    webtoolkit.base64.js
    

    is used to encode Base64 user credentials on the request headers for the Basic Auth.

    The second one

    rest-example.js
    

    is local to GeoServer context and contains our specific utility Javascript functions.

    The rest of the HTML is quite simple, just defines several simple HTML fields and invokes an

    init()
    

    function at startup.

  2. Open with a simple text editor the file rest-example.js. Look for the init() function, which should be the first one

    /**
     * init:function
     * - main initialization function
     **/
    function init(){
     // Initializing the SLD list
     var sldgetrequest=new ajaxRequest();
     sldgetrequest.onreadystatechange=function(){
     if (sldgetrequest.readyState==4){
            if (sldgetrequest.status==200 || window.location.href.indexOf("http")==-1){
              var jsondata=eval("("+sldgetrequest.responseText+")") //retrieve result as an JavaScript object
              var rssentries=jsondata.styles.style;
    
              var sldSelect = document.getElementById('sldSelect');
                      sldSelect.options.length = 0; // clear out existing items
              for (var i=0; i<rssentries.length; i++){
                    var entry = rssentries[i];
                    sldSelect.options.add(new Option(entry.name, i))
              }
              document.getElementById('selectedSld').value=sldSelect.options[0].text;
            }
            else{
              alert("An error has occured making the request");
            }
       }
      }
    
      sldgetrequest.open("GET", "/geoserver/rest/styles.json", true);
      sldgetrequest.setRequestHeader('Authorization', make_base_auth(geoserverUsername, geoserverPassword));
      sldgetrequest.send(null);
    
     // Initializing the Layers list
     var lyrgetrequest=new ajaxRequest();
     lyrgetrequest.onreadystatechange=function(){
     if (lyrgetrequest.readyState==4){
            if (lyrgetrequest.status==200 || window.location.href.indexOf("http")==-1){
              var jsondata=eval("("+lyrgetrequest.responseText+")") //retrieve result as an JavaScript object
              var rssentries=jsondata.layers.layer;
    
              var lyrSelect = document.getElementById('lyrSelect');
                      lyrSelect.options.length = 0; // clear out existing items
              for (var i=0; i<rssentries.length; i++){
                    var entry = rssentries[i];
                    lyrSelect.options.add(new Option(entry.name, i))
              }
              document.getElementById('selectedLyr').value=lyrSelect.options[0].text;
            }
            else{
              alert("An error has occured making the request");
            }
       }
      }
    
      lyrgetrequest.open("GET", "/geoserver/rest/layers.json", true);
      lyrgetrequest.setRequestHeader('Authorization', make_base_auth(geoserverUsername, geoserverPassword));
      lyrgetrequest.send(null);
    }
    

    The function is quite simple, makes two REST GET operations to read styles and layers from GeoServer and populate the select boxes.

    Notice how the JSON responses from GeoServer are parsed and converted to JavaScript objects using the

    eval()
    

    native Javascript function.

    Lets examine the styles JSON response from GeoServer REST API; if you go to this URL on a standard browser

    http://localhost:8083/geoserver/rest/styles.json
    

    you will get a plain string like this

    {
      "styles":{
            "style":[
              {
                    "name":"arealandmarks",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/arealandmarks.json"
              },
              {
                    "name":"arealandmarks_pt",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/arealandmarks_pt.json"
              },
              {
                    "name":"buildings",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/buildings.json"
              },
              {
                    "name":"cemetery_graphics",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/cemetery_graphics.json"
              },
              {
                    "name":"cemetery_mark",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/cemetery_mark.json"
              },
              {
                    "name":"citylimits",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/citylimits.json"
              },
              {
                    "name":"contours",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/contours.json"
              },
              {
                    "name":"countries",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/countries.json"
              },
              {
                    "name":"county",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/county.json"
              },
              {
                    "name":"dem",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/dem.json"
              },
              {
                    "name":"dem2",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/dem2.json"
              },
              {
                    "name":"dem_elevation",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/dem_elevation.json"
              },
              {
                    "name":"hillshade",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/hillshade.json"
              },
              {
                    "name":"lakes",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/lakes.json"
              },
              {
                    "name":"line",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/line.json"
              },
              {
                    "name":"mainrd",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/mainrd.json"
              },
              {
                    "name":"parcels",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/parcels.json"
              },
              {
                    "name":"point",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/point.json"
              },
              {
                    "name":"point_landmark",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/point_landmark.json"
              },
              {
                    "name":"point_landmark_ds",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/point_landmark_ds.json"
              },
              {
                    "name":"point_landmark_ds_ns",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/point_landmark_ds_ns.json"
              },
              {
                    "name":"polygon",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/polygon.json"
              },
              {
                    "name":"raster",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/raster.json"
              },
              {
                    "name":"rivers",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/rivers.json"
              },
              {
                    "name":"river_arrow",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/river_arrow.json"
              },
              {
                    "name":"states_population",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/states_population.json"
              },
              {
                    "name":"streets",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/streets.json"
              },
              {
                    "name":"streets_inner",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/streets_inner.json"
              },
              {
                    "name":"streets_outer",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/streets_outer.json"
              },
              {
                    "name":"trails",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/trails.json"
              },
              {
                    "name":"trails2",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/trails2.json"
              },
              {
                    "name":"wetlands",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/wetlands.json"
              },
              {
                    "name":"wetlands_dyn",
                    "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/wetlands_dyn.json"
              }
            ]
      }
    }
    

    which is converted by eval() to a complex Javascript object, that can be used like a normal variable to inspect and also update it’s properties.

    jsondata.styles.style
    

    can be used to get the styles array. Each entry has a property name which is used later to populate the select box options

    for (var i=0; i<rssentries.length; i++){
          var entry = rssentries[i];
          lyrSelect.options.add(new Option(entry.name, i))
    }
    

    The same works for the layers

    http://localhost:8083/geoserver/rest/layers.json
    

    Note

    Notice that all the request have been issued by setting the authorization

    .setRequestHeader('Authorization', make_base_auth(geoserverUsername, geoserverPassword));
    

    in order to avoid GeoServer to ask for credentials. This is also necessary later for the PUT request, otherwise you will get an authorization exception.

  3. Search now for the for the assignSldToLyr() function inside the rest-example.js

    /**
     * assignSldToLyr:function
     * - retrieves the layer JSON and updates it via REST PUT by modifying the defaultStyle
     */
    function assignSldToLyr(){
      var sld = document.getElementById('selectedSld').value;
      var lyr = document.getElementById('selectedLyr').value;
      if (confirm('Assign SLD: "' + sld + '" to Layer: "' + lyr + '" ?')){
            // retrieving Layer JSON
            var layer;
            var layerAjaxRequest=new ajaxRequest();
            layerAjaxRequest.onreadystatechange=function(){
              if (layerAjaxRequest.readyState==4){
                    if (layerAjaxRequest.status==200 || window.location.href.indexOf("http")==-1){
                      var jsondata=eval("("+layerAjaxRequest.responseText+")") //retrieve result as an JavaScript object
                      layer=jsondata.layer;
                    }
                    else{
                      alert("An error has occured making the request");
                    }
              }
            }
            layerAjaxRequest.open("GET", "/geoserver/rest/layers/"+lyr+".json", false); //synchronous request
            layerAjaxRequest.setRequestHeader('Authorization', make_base_auth(geoserverUsername, geoserverPassword));
            layerAjaxRequest.send(null);
    
            if(confirm('I\'m going to change the style of the layer "' + lyr + '" from "' + layer.defaultStyle.name + '" to "' + sld + '". Would you like to proceed?')){
               // performing PUT (update) request to change the Layer default style
               var mypostrequest=new ajaxRequest();
               mypostrequest.onreadystatechange=function(){
                     if (mypostrequest.readyState==4){
                       if (mypostrequest.status==200 || window.location.href.indexOf("http")==-1){
                             alert("Done!");
                       }
                       else{
                             alert("An error has occured making the request");
                       }
                     }
               }
    
               // updating layer defaultStyle
               layer.defaultStyle.name=sld;
    
               // sending out the request
               mypostrequest.open("PUT", "/geoserver/rest/layers/"+lyr+".json", true);
               mypostrequest.setRequestHeader("Content-type", "application/json");
               mypostrequest.setRequestHeader('Authorization', make_base_auth(geoserverUsername, geoserverPassword));
               mypostrequest.send('{"layer":' + JSON.stringify(layer) + '}');
            }
      }
    }
    

    This function is quite interesting. It executes two REST operation: a GET in order to retrieve the selected layer details in JSON, and a PUT in order to update the defaultStyle by modifying the JSON layer representation and sending back the updated string to GeoServer.

    The JSON representation of a layer can be obtained simply by issuing a HTTP GET request like this

    http://localhost:8083/geoserver/rest/layers/bstreets.json
    

    you will get back something like

    {
      "layer":{
            "name":"bstreets",
            "type":"VECTOR",
            "defaultStyle":{
              "name":"streets",
              "href":"http:\/\/localhost:8083\/geoserver\/rest\/styles\/streets.json"
            },
            "resource":{
              "@class":"featureType",
              "name":"bstreets",
              "href":"http:\/\/localhost:8083\/geoserver\/rest\/workspaces\/geosolutions\/datastores\/boulder_shapefiles\/featuretypes\/bstreets.json"
            },
            "enabled":true,
            "opaque":false,
            "attribution":{
              "logoWidth":0,
              "logoHeight":0
            }
      }
    }
    

    The function simply updates the layer defaultStyle with the one selected by the user

    // updating layer defaultStyle
    layer.defaultStyle.name=sld;
    

    and then issues an authorized HTTP PUT request back to GeoServer at the same json layer URL

    // sending out the request
    mypostrequest.open("PUT", "/geoserver/rest/layers/"+lyr+".json", true);
    mypostrequest.setRequestHeader("Content-type", "application/json");
    mypostrequest.setRequestHeader('Authorization', make_base_auth(geoserverUsername, geoserverPassword));
    mypostrequest.send('{"layer":' + JSON.stringify(layer) + '}');
    

    Note

    It’s quite interesting the use of

    JSON.stringify(layer)
    

    in order to convert the JSON Object back to a plain string. We also need to insert the

    {"layer": <body here> }
    

    strings in order to match the original format, since we filtered out before the “layer” part by doing this

    layer=jsondata.layer;
    

    Notice also that the URL address for the update operation is the same we used previously to retrieve the layer Json representation

    /geoserver/rest/layers/"+lyr+".json
    

    which in the practice is equivalent to something like

    http://localhost:8083/geoserver/rest/layers/<selected_layer>.json