Namespaces

Variants
Actions

Please note that as of October 24, 2014, the Nokia Developer Wiki will no longer be accepting user contributions, including new entries, edits and comments, as we begin transitioning to our new home, in the Windows Phone Development Wiki. We plan to move over the majority of the existing entries over the next few weeks. Thanks for all your past and future contributions.

Revision as of 05:02, 20 July 2012 by hamishwillee (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Accessing RESTful Web Services with JavaScript

From Wiki
Jump to: navigation, search
Article Metadata
Article
Created: mfabiop (26 Oct 2009)
Last edited: hamishwillee (20 Jul 2012)

Contents

Introduction

The most used architecture to develop WRT widgets is shown in the below picture. There is a server that provides some useful data and the widget consumes that data. The communication between the widget and the server application can be done in a restful web service. This post is about the definition of a restful web service specification and the implementation of a javascript library to access that web service.

Arquitetura3.png

The RESTful Web Service Specification

A RESTful web service (also called a RESTful web API) is a simple web service implemented using HTTP and the principles of Representational state transfer (REST). This is a style of software architecture for distributed hypermedia systems such as the World Wide Web.

In a RESTful web service the developer has access to “resources” and that “resources” can be created, retrieved, updated or deleted. The definition of RESTful web service can be thought of as comprising three aspects:

  • The base URI for the web service such as http://www.iana.org/domains/example/
  • The MIME type of the data supported by the web service. This is often JSON, XML or YAML but can be any other valid MIME type.
  • The set of operations supported by the web service using HTTP methods (e.g. POST, GET, PUT or DELETE).

The following table shows how the HTTP verbs are typically used to implement a RESTful web service.

Resource GET PUT POST DELETE
Collection URI such as http://www.iana.org/domains/example/ List the members of the collection complete with their member URIs for further navigation. For example list all the cars for sale. Meaning defined as ‘replace the entire collection with another collection’. Create a new entry in the collection where the ID is assigned automatically by the collection. The ID created is usually included as part of the data returned by this operation. Meaning defined as ‘delete the entire collection’.
Member URI such as http://www.iana.org/domains/example/ Retrieve a representation of the addressed member of the collection expressed in an appropriate MIME type. Update the addressed member of the collection or create it with the specified ID. Treats the addressed member as a collection in its own right and creates a new subordinate of it. Delete the addressed member of the collection.

The Specification

It was specified a restful web service using the HTTP methods shown above. For each CRUD( Create, Read, Update, Delete ) operation has been defined a means for all possible HTTP responses as shown below:

PUT HTTP method (Create)

  • Success = 200
  • The operation has been accepted, but not executed yet = 202
  • Client error = 4xx
    • Data conflict = 409
    • Forbidden operation = 403
  • Server error = 5xx
    • Default = 500
    • Overload = 503

GET HTTP method (Retrieve)

  • Success = 200
  • Client error = 4xx
    • Resource not found = 404
    • Forbidden operation = 403
  • Server error = 5xx
    • Default = 500
    • Overload = 503

POST HTTP method (Update)

  • Success = 200
  • The operation has been accepted, but not executed yet = 202
  • Client error = 4xx
    • Resource not found = 404
    • Data conflict = 409
    • Forbidden operation = 403
  • Server error = 5xx
    • Default = 500
    • Overload = 503

DELETE HTTP method (Delete)

  • Success = 200
  • The operation has been accepted, but not executed yet = 202
  • Client error = 4xx
    • Resource not found = 404
    • Forbidden operation = 403
  • Server error = 5xx
    • Default = 500
    • Overload = 503

Each http method used is shown in the follow list:

  • 200 - Created - Resource has been created.
  • 202 - Accepted - Operation has been accepted, but not executed yet. May be stored to be executed in the future.
  • 403 - Forbidden - Forbidden access, the code 404 can be returned for security reasons.
  • 409 - Not Found - Resource not found.
  • 500 - Internal Server Error - A server error.
  • 503 - Service Unavailable - The server is busy and the client can configure the Retry-After:xx (xx in seconds) parameter in the HTTP response.

Implementation

JavaScript client

The client code to access the RESTful web service was developed with just one class. Each CRUD operation has two listeners. One method is executed when a success occurs; and the other is executed when an error occurs. For example, onRetrieveError will be executed if a required resource is not found in the server and the HTTP code 404 is returned. There are the following listeners in the RestfulResource class:

  • onCreateSuccess(responseText)
  • onCreateError(responseStatus)
  • onRetrieveSuccess(responseText)
  • onRetrieveError(responseStatus)
  • onUpdateSuccess(responseText)
  • onUpdateError(responseStatus)
  • onDeleteSuccess(responseText)
  • onDeleteError(responseStatus)

The CRUD operations:

  • create(jsonObject)
  • retrieve(id)
  • update(jsonObject)
  • delete(id)

The RestfulResource class

function RestfulResource(resource_url){
 
this.resource_url = resource_url
 
this.xmlhttp = new XMLHttpRequest();
 
/**
* Get the resource or a list of resources calling the RESTful web service with the GET http method
* @param id The id of the resource, if is null a list of resources will be retrieved.
*/

this.retrieve = function(id){
var url = this.resource_url
if(id != null)
url = this.resource_url.concat("/"+id)
 
var self = this;
this.xmlhttp.onreadystatechange=function(){
if (self.xmlhttp.readyState==4){
if (self.xmlhttp.status==200){
self.onRetrieveSuccess.call(self,self.xmlhttp.responseText);
}else{
self.onRetrieveError.call(self,self.xmlhttp.statusText);
}
}
}
this.xmlhttp.open("GET",url,true);
this.xmlhttp.send(null);
}
 
/*
* The method called when a resource is successfully retrieved.
*/

this.onRetrieveSuccess = function(responseText){
alert("onRetrieveSuccess method "+responseText);
}
 
/*
* The method called when a resource is not created.
*/

this.onRetrieveError = function(statusText){
alert("onRetrieveError method "+statusText);
}
 
/**
* Create the resource calling the RESTful web service with the PUT http method
* @param jsonObject The jsonObject that will be created.
*/

this.create = function(jsonObject){
var jsonString = JSON.stringify(jsonObject);
var self = this;
this.xmlhttp.onreadystatechange=function(){
if (self.xmlhttp.readyState==4){
if (self.xmlhttp.status==200){
self.onCreateSuccess.call(self,self.xmlhttp.responseText);
}else{
self.onCreateError.call(self,self.xmlhttp.statusText);
}
}
}
this.xmlhttp.open("PUT",this.resource_url,true);
this.xmlhttp.setRequestHeader("Content-type", "application/json");
this.xmlhttp.setRequestHeader("Content-length", jsonString.length);
this.xmlhttp.setRequestHeader("Connection", "close");
this.xmlhttp.send(jsonString);
}
 
/*
* The method called when the resource is successfully created.
*/

this.onCreateSuccess = function(responseText){
alert("onCreateSuccess method "+responseText);
}
 
/*
* The method called when the resource can't be created.
*/

this.onCreateError = function(statusText){
alert("onCreateError method "+statusText);
}
 
/**
* Update a resource calling the RESTful web service with the POST http method
* @param id The id of the resource, if is null a list of resources will be retrieved.
* @return The retrieved resource.
*/

this.update = function(jsonObject){
var jsonString = JSON.stringify(jsonObject);
var self = this;
this.xmlhttp.onreadystatechange=function(){
if (self.xmlhttp.readyState==4){
if (self.xmlhttp.status==200){
self.onUpdateSuccess.call(self,self.xmlhttp.responseText);
}else{
self.onUpdateError.call(self,self.xmlhttp.statusText);
}
}
}
this.xmlhttp.open("POST",this.resource_url,true);
this.xmlhttp.setRequestHeader("Content-type", "application/json");
this.xmlhttp.setRequestHeader("Content-length", jsonString.length);
this.xmlhttp.setRequestHeader("Connection", "close");
this.xmlhttp.send(jsonString);
}
 
/**
* The method called when the resource is successfully updated.
*/

this.onUpdateSuccess = function(responseText){
alert("onUpdateSuccess method "+responseText);
}
 
/**
* The method called when the resource can't be updated.
*/

this.onUpdateError = function(statusText){
alert("onUpdateError method "+statusText);
}
 
/**
* Remove a resource calling the RESTful web service with the DELETE http method
* @param id The id of the resource.
*/

this.remove = function(id){
var url = this.resource_url.concat("/"+id);
var self = this;
this.xmlhttp.onreadystatechange=function(){
if (self.xmlhttp.readyState==4){
if (self.xmlhttp.status==200){
self.onRemoveSuccess.call(self,self.xmlhttp.responseText);
}else{
self.onRemoveError.call(self,self.xmlhttp.statusText);
}
}
}
this.xmlhttp.open("DELETE",url,true);
this.xmlhttp.send(null);
}
 
/**
* The method called when the resource is successfully removed.
*/

this.onRemoveSuccess = function(responseText){
alert("onRemoveSuccess method "+responseText);
}
 
/**
* The method called when the resource can't be removed.
*/

this.onRemoveError = function(statusText){
alert("onRemoveError method "+statusText);
}
 
}

The RESTful web services (PHP and Grails)

Here, there are some implementation examples of web services using the specification above. As you can see, there are simple and small, very easy to understand. They implement the CRUD operations on an Author class, with only two attributes (id and name).

PHP

<?php
require "JSON.php";
$id = $_SERVER["PATH_INFO"];
$method = $_SERVER["REQUEST_METHOD"];
$json = new JSON;
$conn = mysql_connect('HOST', 'USER', 'PASSWORD');
 
if (!$conn) {
die('Could not connect: ' . mysql_error());
}
mysql_select_db("DATABASE");
 
class Author {
var $id;
var $name;
 
function Author($id, $name) {
$this->id = $id;
$this->name = $name;
}
}
 
switch($method){
case "GET":
if(isset($id)){
$result = mysql_query('SELECT * FROM author WHERE id = '.substr($id,1));
}else{
$result = mysql_query('SELECT * FROM author');
}
$jsonObjects = array();
while($row = mysql_fetch_array($result)){
$author = new Author($row['id'],$row['name']);
array_push($jsonObjects,$author);
}
if(count($jsonObjects) > 0){
echo $json->serialize( $jsonObjects );
}else{
header("HTTP/1.1 404 Not Found");
}
break;
 
case "PUT":
$body = @file_get_contents('php://input');
$newJsonObject = $json->unserialize( $body, true );
$insertAuthor = 'INSERT INTO author (name)VALUES(\''.$newJsonObject->name.'\')';
mysql_query($insertAuthor);
echo "Created";
break;
 
case "POST":
$body = @file_get_contents('php://input');
$editJsonObject = $json-&gt;unserialize( $body, true );
$result = mysql_query('SELECT * FROM author WHERE id = '.$editJsonObject->id);
if($result == false){
header("HTTP/1.1 404 Not Found");
break;
}
$updateAuthor = 'UPDATE author set name = \''.$editJsonObject->name.'\' where id = '.$editJsonObject->id;
if(!mysql_query($updateAuthor)){
header("HTTP/1.1 500 Internal Server Error");
break;
}
break;
 
case "DELETE":
if(isset($id)){
if(mysql_num_rows(mysql_query('SELECT * FROM author WHERE id = '.substr($id,1))) == 0){
header("HTTP/1.1 404 Not Found");
break;
}
$deleteAuthor = "DELETE FROM author WHERE id = ".substr($id,1);
if(!mysql_query($deleteAuthor)){
header("HTTP/1.1 500 Internal Server Error");
break;
}
}else{
header("HTTP/1.1 404 Not Found");
break;
}
break;
}
mysql_close($conn);
?>

Grails

import grails.converters.*
 
class RestfulController {
 
def authors = {
def method = request.getMethod()
def jsonObject = null;
switch(method){
case "GET":
if(!params.id){
def result = Author.list()
response.status = 200
render result as deep.JSON
}else{
def result = Author.findById(params.id)
if(result){
response.status = 200
render result as deep.JSON
}else{
response.status = 404
render "Entity not found"
}
}
break;
 
case "POST":
if(params.json)
jsonObject = JSON.parse(params.json)
if(jsonObject.id){
response.status = 200
def entity = Author.findById(jsonObject.id)
if(entity){
entity.properties = jsonObject
if(entity.save()){
response.status = 200
render "Updated"
}else{
response.status = 500
render "Entity not updated"
}
}else{
response.status = 404
render "Entity not found"
}
}else{
response.status = 404
render "Entity not found"
}
break;
 
case "PUT":
if(params.json)
jsonObject = JSON.parse(params.json)
def entity = new Author(jsonObject)
if(entity.save()){
response.status = 200
render "Created"
}else{
response.status = 500
render "Entity not saved"
}
break;
 
case "DELETE":
if(params.id){
def entity = Author.findById(params.id)
if(entity){
try {
entity.delete(flush:true)
response.status = 200
render "Deleted"
}catch(org.springframework.dao.DataIntegrityViolationException e) {
response.status = 500
render "Entity not deleted"
}
}else{
response.status = 404
render "Entity not found"
}
}else{
response.status = 404
render "Entity not found"
}
break;
}
}
}


Downloads

A WRT example widget using the JavaScript library to access a RESTful web service (widget.wgz).

References

This page was last modified on 20 July 2012, at 05:02.
433 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×