speich.net logo

Tutorial Part 1: REST with Dojo and PHP

February 13th, 2010 Simon Leave a comment Go to comments

I only started recently to dig into REST, so I’m in no way an expert in this field. There are not that many tutorials on the internet, and the ones I found were only of limited help. They all seemed to miss something or, rather, I missed something. So I decided to write my own tutorial about my findings to help others that might have the same problems. On the client side, I’ll use the Dojo Toolkit, on the server side, PHP.

I will focus mainly on providing and explaining some working code examples including server PHP code. At the end of this article you can find all code as a downloadable zip-file. As an introduction to REST, read some of the tutorials linked at the end of this article, because I’m not going to repeat some of the things you can read there.

1. Url-confusion

What confused me the most at the beginning was the structure of the REST urls, for example in the article Create a REST API with PHP. How on earth can an url be structured like:

PUT request to /api/users/1 – Update user with ID of 1

Where’s the PHP page in this url that receives this request? And second, I always thought variables had to be posted with a querystring. It took me a while to figure out that the path in the url does not necessarily have to end with a filename such as mypage.php and that in this example it is simply omitted. Furthermore, /api/users/1 is not a sort of variable, but just part of the path to the resource. To make this clear, just add a PHP filename in front of the resource…

mypage.php/api/users/1

… and then you can process the request on the page mypage.php with:

$resource = $_SERVER['PATH_INFO'];
$method   = $_SERVER['REQUEST_METHOD'];

Where the variable $resource would contain:

/api/users/1

and $method would contain:

PUT

To extract additional variable data from the request body or request header you can use parse_string() and file_get_contents() or $_GET.

if ($method == 'POST' || $method == 'PUT') {
  parse_str(file_get_contents('php://input'), $_DATA);
}
else {
  $_DATA = $_GET;
}

The above code is exactly what I use in the demo file php-rest-check.php:

<?php
$resource = $_SERVER['PATH_INFO'];
$method = $_SERVER['REQUEST_METHOD'];

 if ($method == 'POST' || $method == 'PUT') {
  parse_str(file_get_contents('php://input'), $_DATA);
}
else {
  $_DATA = $_GET;
}

$arr = array(
  'method' => $method,
  'resource' => $resource,
  'data' => $_DATA
);	

// send (fake) HTTP status responses if forceError is set
switch($method) {
  case 'POST':
   if ($_DATA['forceError'] == 'false') {
    $status = 'HTTP/1.1 201 Created';
  }
  else {
    $status = 'HTTP/1.1 500 Internal Server Error';
  }
  break;
  case 'GET':
    if ($_DATA['forceError'] == 'false') {
       $status = 'HTTP/1.1 200 OK';
    }
    else {
      $status = 'HTTP/1.1 500 Internal Server Error';
    }
    break;
  case 'PUT':
    if ($_DATA['forceError'] == 'false') {
      $status = 'HTTP/1.1 200 OK';
    }
    else {
      $status = 'HTTP/1.1 404 Not Found';
    }
    break;
  case 'DELETE':
    if ($_DATA['forceError'] == 'false') {
      $status = 'HTTP/1.1 200 OK';
      // With 204 there would be no status code available on the client-side
      //$status = 'HTTP/1.1 204 No Content';
    }
    else {
      $status = 'HTTP/1.1 500 Internal Server Error';
    }
    break;
}
header($status);
if ($_DATA['forceError'] == 'false') {
  // only send response data back when successful
  echo json_encode($arr);
}
?>

2. Sending the RESTful requests

The only remaining question now is how you send a PUT or DELETE request. For that I’ll use the Dojo Toolkit, because that makes it very easy and I’m a big fan of it. If you don’t know Dojo, sorry, but it’s worth learning. The demo is meant as a working example to check if your server provides all four methods. The toolkit provides for each its own method, e.g. dojo.xhrPost(), dojo.xhrGet(), dojo.xhrPut() and dojo.xhrDelete().

First we define a general argument object for all requests, because some attributes are all the same. If you want to send some variables along with your request, just add them as the content property. Dojo will either send them as part of the request header (e.g. in the querystring) in case of a GET/DELETE or as part of the request body in case of a PUT/POST. Use firebug to check what I mean. I added a forceError variable to tell PHP to send fake HTTP error messages.

<script type="text/javascript">
var demo = {
  // general arguments object for the different xhr methods.
  args: {
    url: 'php-rest-check.php',
    handleAs: 'json',
    content: {
      forceError: false,    // if set to true, server responds with an HTTP error
      var1: 'someVal1',   // just some variables
      var2: 'someVal2'
    },
    load: function(response, ioArgs) {
      // log response
      dojo.byId('log').innerHTML+= '<p>HTTP status: ' + ioArgs.xhr.statusText + ' ' + ioArgs.xhr.status + '<br/>' +
        'method: <strong>' + response.method + '</strong><br/>' +
        'resource: ' + response.resource + '<br/>' +
        'query: ' + dojo.objectToQuery(response.data) + '</p>';
    },
    error: function(error) {
      // log HTTP error status
      dojo.byId('log').innerHTML+= '<p>HTTP error status: ' + error.status;
    }
  },
...

Then, all that’s left is that you need to slightly adapt the args object for each request. Note the dojo.clone() method. I use it to work with a copy not a reference of the args object.

...
  // create
  post: function() {
    var args = dojo.clone(this.args);    // otherwise we would keep adding '/name/1' with each new post
    args.url+= '/images/1'; // create new item with id = 1 at '/images'
    dojo.xhrPost(args);
  },
  // read/load
  get: function() {
    var args = dojo.clone(this.args);
    args.url+= '/images';    // load all items at '/images'
    dojo.xhrGet(args);
  },
  // update
  put: function() {
    var args = dojo.clone(this.args);
    args.url+= '/images/1'; // update item with id = 1 at '/images
    dojo.xhrPut(args);
  },
  // delete
  del: function() {
    var args = dojo.clone(this.args);
    args.url+= '/images/1';    // delete item with id = 1 at resouce
    dojo.xhrDelete(args);
  }
}
...

Finally, add the calls to the links on page load:

...
dojo.addOnLoad(function() {
  // add each request method to one of the links
  dojo.query('#ulDemo li').forEach(function(node, i) {
    dojo.connect(node, 'onclick', function(e) {
      dojo.stopEvent(e);	// prevent link action
      demo.args.content.forceError = dojo.byId('fldforceError').checked ? true : false;
      switch(i) {
        case 0:
          demo.post();
          break;
        case 1:
          demo.get();
          break;
        case 2:
          demo.put();
          break;
        case 3:
          demo.del();	// note: delete is reserved word
          break;
        }
     });
  });
});
</script>

Tutorials

Downloads

zipped demo file of part 1 php-rest-check.zip

zipped demo file of part 2 dojo-jsonreststore.zip

That’s the end of the first part. Second part in preparation…

Categories: JavaScript, PHP Tags: ,
  1. No comments yet.
  1. No trackbacks yet.
CAPTCHA Image CAPTCHA Audio
Refresh Image

© 2009 speich.net, Konzept und Programmierung Simon Speich.

Die Artikel auf dieser Seite laufen mit WordPress. Theme by NeoEase