I developed a very quick and simple PHP application for reading classified ads from an XML file and allowing the user to perform CRUD operations on it (this was a homework assignment).
I'm now tasked with developing this application to a RESTful service. The professor actually doesn't seem to have any experience with RESTful services, because he said my application was find to hand in for the next assignment, when my research indicates it doesn't really fulfil all the RESTful requirements.
Regardless, I want to do this correctly for learning purposes, even if I can hand in my old assignment and get a good grade. I'm having trouble learning where to begin though; I'm not sure exactly what a RESTful service really is.
I think the best way to get advice is to post sample code from my previous assignment to see how I handled things and how I need to handle things instead.
For instance, here is how I create new classifieds.
Create.php
//Basically just a list of <INPUT TYPE = "text" NAME = "something"> in the <body> fields
CreateSuccess.php
<html><head><?php $simplerXML = simplexml_load_file('file.xml');
//Generate the basic ad information
$newAd = $simplerXML->addChild('advertisement','');
$newAd->addAttribute('category', $_POST["category"]);
$title = $newAd->addChild('title', $_POST["title"]);
$title->addAttribute('ID', $_POST["ID"]);
$pageTitle = $newAd->addChild('pagetitle', $_POST["pagetitle"]);
//etc, for all the SUBMIT boxes
//save the XML
$simplerXML->asXML('file.xml');
echo "<script type='text/javascript'>
//redirect back to ad listing page
window.onload = function () { top.location.href = 'ads.php'; };
</script>";
?></head>
<body></body></html>
I'm also using URL parameters for the RUD actions. I've heard URL parameters are not allowed as well?
Thanks.
EDIT:
So the SWITCH statement, does it go in the index.php file? And then each case will call a function, ie CreateXML for the POST method?
Then the parameters it needs will be the object type, object id, and content type? How do I get the values to update the XML with, do I just send it to the Create.php file containing the list of input boxes?
If your service supports all CRUD operations, it's always advisable to implement a RESTful interface. It's not super-hard to do so. I've outlined some of the basics below.
A RESTful service simply does a few things:
- It uses HTTP request method for communication of the CRUD action
- It uses HTTP status code to communicate response status, and
- It uses the URI to define your resource (file, database item you're accessing, etc).
- It is stateless
The idea is to minimize the development of custom communications for these things that are already defined in the HTTP spec.
1 - REQUEST METHOD
The 4 HTTP request methods you're required to support for a RESTful service are:
- POST
- GET
- PUT
- DELETE
and you may optionally support
- PATCH
- HEAD
You can map these directly to your CRUD actions as follows:
- POST = Create
- GET = Retrieve
- PUT = Update
- DELETE = Delete
- PATCH = Edit (a partial update, e.g. "change password". PUT becomes "replace")
- HEAD = Header only (metadata about the resource)
To do this, route requests properly with a simple request method router as follows:
switch ($_SERVER["REQUEST_METHOD"]) {
case "POST":
// Create action
break;
case "GET":
// Retrieve action
break;
case "PUT":
// Update action
break;
case "DELETE":
// Delete action
break;
}
2 - STATUS CODE
You should further implement HTTP status codes from your service to communicate status back to the client, e.g.:
- 20x = success
- 30x = redirection
- 40x = communication issues
- 50x = server error
To do this, simply prepend your response with the proper HTTP header output, e.g.:
header("Status: 500 Internal Server Error");
You can reference the full list of implemented HTTP status codes here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
3 - URIs
For URIs, RESTful services usually follow a top-down approach to categorized naming, e.g.
/object_type/id.content_type
Examples:
POST /user
PUT /user/1
GET /user/1.json
GET /user/1.html
You can implement a very rudimentary RESTful router for the above convention using Apache with mod_rewrite
in an .htaccess
file, as follows:
RewriteEngine On
RewriteRule ^([^\/]+)\/([^\.]+)\.(\w+)$ index.php?object_type=$1&object_id=$2&content_type=$3
You would then have index.php
Look for the appropriate object_type and id to route appropriately, e.g.:
$object = $_GET["object_type"];
$id = (int) $_GET["object_id"];
$content_type = $_GET["content_type"];
// Route from here to a class with the name of the object (e.g. UserController) via __autoload
// or to a file (e.g. user.php) via include, and pass id and content_type as params
4 - STATELESSNESS
Simply stated, the server maintains no "state" for the client. No requirements for storing session or status. Each request represents a complete transaction. I.e. if I GET user/1, the server won't remember that I did that, and future requests won't be dependent upon or affected by previous ones.
If you implement these standards, congrats, you've built a RESTful service!
"RESTful" is a broad concept and there are degrees of "RESTfulness". Wikipedia is a good guide here
Here are some higher-level characteristics not addressed in the other answer (which is also good):
- Resources are available at URLs, preferably with only one canonical url per resource.
- You can indicate that a resource is available at other urls or representations using the
Content-Location
header.
- You can provide different representations of a resource (html, json, xml, etc) by using content negotiation with the
Accept
and content-type
headers.
- Changes in the state of the resource are entirely represented by a single HTTP request. The server does not need to maintain state to service a client request. Consequently, requests can be easily proxied and cached.
- An example of a common violation of this principle is a url like "http://example.org/profile" which serves a different user profile depending on who is logged in.
- Better would be to separate the resource from authorization: "http://example.org/profile/{USERID}" would always serve a particular user's userid, but return 401 (Not authorized) if the client does not have permission. (Also, authorization information should be sent with every request, so that the server does not require a session token or similar server-side state. As a consequence, most websites with a cookie-based login system are not purely restful.)
- GET to retrieve a resource. This should not change the state of the resource and should be safely repeatable. This property is often called "Idempotency".
- PUT to update a resource. Using techniques such as conditional updating requests (PUT or DELETE with an
if-*
header) you can even implement optimistic concurrency control.
- DELETE to remove a resource.
- POST as a catchall "do something" resource. POST is used when you have to do something that doesn't fit cleanly into the http methods or when you need to perform an action with side-effects (for example, create a new resource without knowing its name or implement an RPC protocol.) However, you are expected to use http headers and response codes to show what resource side-effects there were, e.g. a "201 Created" with
Location
and Content-Location
headers and a list of urls affected by the change.
- Resource representations are self-describing "hypertext" with links to other resources.
- An example of a violation of this principle: Suppose "http://example.com/articles" is a list of articles and the json representation of it looks like
[1,2,3,4,5,6]
. This is a list of article ids, but it's not self-describing or hypertext--the client needs to know this is a list of article ids, and it needs to know that to get an article resource it has to construct a url like "http://example.org/articles/1".
- Better would be a response like
{"articles":[{"id":1,"url":"http://example.org/articles/1"},...]}
. Like html, a client using a rest service should only have to follow links (not make links) to get at other related resources. You can even document the available methods you can use to manipulate a resource--create, update, delete, etc.