Category: REST

New Features on Recess Edge

Posted by Kris | Comments ( 0 ) | Filed in: News, REST | | | | |

We're approaching the 0.2 release quickly. It represents the biggest leap in functionality for the framework yet. We've just pushed two major, related features to Recess Edge on Github: a new view selection process and content-negotiation. For folks who like to try development bits, when checking out Edge, here are some pointers on what to expect with the most recent commits:

New View Selection System based on Requested Format

The new native view system looks for an application's view template by matching a format to a file-extension. So, for example, if a visitor requests '/foo.xml' and your controller returns a view-name of 'foo', Recess now looks for the view template foo.xml.php - the same goes with json, rss, pdf, etc. For the default html format there is no additional extension, it's still just foo.php.

What about automatic JSON? It is no longer turned on by default (though, if you are using existing code, it should still work in version 0.20 but is now considered deprecated and will not be supported in 0.30). Recess has a new, more advanced mechanism for selecting views. Multiple views can be registered with a controller with the new !RespondsWith annotation, it looks like this:

/**
  * !RespondsWith Layouts, Json
  * !Prefix my/
  */
class MyController extends Controller {
   function foo() { }
}

The !Prefix annotation will be discussed shortly. Layouts and Json are both views with class names LayoutsView, JsonView respectively. These form the prioritized list which Recess will use to find a view. Suppose 'my/foo.json' is requested by the client. In this case LayoutsView will be asked first 'can you render this response?', if the view template 'my/foo.json.php' exists then answer is yes and it will render and we're done. If that file does not exist, though, then the new JsonView will be asked 'can you render this response?' and it can always render  a json request so it will. If you *always* wanted to use automatic-json for json requests then you could simply reverse the order: Json, Native. This avoids the cost of checking the file system for a json template file.

A list of the supported formats is provided in the following section.

More RESTful Goodness with Accept-based PHP Content-Negotiation

Recess now has support for parsing the HTTP Accept header (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html) for content-negotiation! This means for folks using RIAs and smart-clients you can interact with your Recess applications truer to the way HTTP intends. Instead of appending a format like '.json' to a URL your clients can now use 'Accept: application/json'. Here are a list of supported mime-types and the formats they map to:

html = text/html, application/xhtml+xml
xml = application/xml, text/xml, application/x-xml
json = application/json, text/x-json application/jsonrequest
js = text/javascript, application/javascript, application/x-javascript
css = text/css 
rss = application/rss+xml 
yaml = application/x-yaml, text/yaml 
atom = application/atom+xml 
pdf = application/pdf
text = text/plain
png = image/png 
jpg = image/jpeg, image/pjpeg 
gif = image/gif
form = multipart/form-data
url-form = application/x-www-form-urlencoded
csv = text/csv

In the rare case you need to accept a MIME-types not listed above you can add it to the mix with the following: MimeTypes::register('foo', 'application/foo'); The second argument can also be an array to support multiple mime types referring to the same format.

New Controller Annotation: !Prefix

It has become a common case in Recess development for the view and route prefix to be the same. A new annotation allows you to specify both with a single annotation:

!Prefix foo/     (now relative routes and views are prefixed with foo/)
!Prefix Routes: /, Views: home/      (allows you to set them individually)

Note: Deprecated Controller Annotations: !View, !RoutesPrefix

The !View annotation is still supported in 0.2 but is deprecated and will be unsupported in v0.3 - It is replaced by !RespondsWith

The !RoutesPrefix annotation is still supported in 0.2 but is deprecated and replaced by !Prefix

Warning: Not-yet-backwards-compatible: PUTing / POSTing JSON

Work is still being done on data that is PUT/POSTed to the server with alternate MIME-types. For folks who depend on POSTing/PUTing JSON please hold off on pulling the latest bits from Edge. This is coming soon and will allow other content-type parsers (XML, for example) to be plugged-in. We're also decoupling the format of input from output, so, for example, you could post JSON and ask for HTML back.

We're getting really close to the 0.2 release and we need your help in testing the bits, reporting and submitting patches for bugs, and contributing to the documentation. Can't wait to get 0.2 out the door!

 

Towards RESTful PHP - 5 Basic Tips

Posted by Kris Jordan | Comments ( 1 ) | Filed in: REST, Routing | | | | |

What is REST?
REST is an architectural style, or set of conventions, for web applications and services that centers itself around resource manipulation and the HTTP spec. Web apps have traditionally ignored the HTTP spec and moved forward using a subset of the protocol: GET and POST, 200 OKs and 404 NOT FOUNDs. As we entered a programmable web of applications with APIs the decision to ignore HTTP gave us problems we’re still dealing with today. We have an internet full of applications with different interfaces (GET /user/1/delete vs. POST /user/delete {id=1}). With REST we can say /user/1 is a resource and use the HTTP DELETE verb to delete it. For more detail on REST check out wikipedia and “quick pitch“.

Tip #1: Using PUT and DELETE methods

In PHP you can determine which HTTP method was used with: $_SERVER['REQUEST_METHOD']; From web browsers this will be either GET or POST. For RESTful clients applications need to support PUT and DELETE (and ideally OPTIONS, etc.) as well. Unfortunately PHP doesn’t have $_PUT and $_DELETE variables like it does $_POST and $_GET. Here’s how to access the content of a PUT request in PHP:

$_PUT  = array();
if($_SERVER['REQUEST_METHOD'] == 'PUT') {
    parse_str(file_get_contents('php://input'), $_PUT);
}

Tip #2: Send Custom HTTP/1.1 Headers

PHP’s header function allows custom HTTP headers to be sent to the client. The HTTP/1.x header contains the response code from the server. PHP will, by default, send back a 200 OK status code which suggests that the request has succeeded even if it has die()’ed or a new resource has been created. There are two ways to change the status code of your response:

header('HTTP/1.1 404 Not Found');
/* OR */
header('Location: http://www.foo.com/bar', true, 201); // 201 CREATED

The first line is a generic way of setting the response status code. If your response requires another header, like the Location header to the resource of a ‘201 Created’ or ‘301 Moved Permanently’, placing the integer status code in the third parameter of header is a shortcut. It is the logical equivalent of the following example, which is easier to read at the cost of being an extra line of code.

header('HTTP/1.1 201 Created');
header('Location: http://www.foo.com/bar');

Tip #3: Send Meaningful HTTP Headers

Policy for deciding when it is appropriate to send each HTTP status code is a full post on its own and the HTTP spec leaves room for ambiguity. There are many other resources on the net which provide insights so I’ll just touch on a few.

201 Created is used when a new resource has been created. It should include a Location header which specifies the URL for the resource (i.e. books/1). The inclusion of a location header does not automatically forward the client to the resource, rather, 201 Created responses should include an entity (message body) which lists the location of the resource.

202 Accepted allows the server to tell the client “yeah, we heard your order, we’ll get to it soon.” Think the Twitter API on a busy day. Where 201 Created implies the resource has been created before a response returns, 202 Accepted implies the request is ok and in a queue somewhere.

304 Not Modified in conjunction with caching and conditional GET requests (requests with If-Modified-Since / If-None-Match headers) allows web applications to say “the content hasn’t changed, continue using the cached version” without having to re-render and send the cached content down the pipe.

401 Unauthorized should be used when attempting to access a resource which requires authentication credentials the request does not carry. This is used in conjunction with www-authentication.

500 Internal Server Error is better than OK when your PHP script dies or reaches an exception.

In the Recess! Framework I use this StatusCodes class to provide named constants for all HTTP/1.1 status codes. Example usage:

header(StatusCodes::httpHeaderFor(StatusCodes::HTTP_NOT_FOUND));

Tip #4: Don’t Use $_SESSION

A truly RESTful PHP application should be entirely stateless- all requests should contain enough information to be handled without additional server side state. In practice this means storing authentication information in a cookie with a timestamp and a checksum. Additional data can also be stored in a cookie. In the event you need more than a cookie’s worth of data fall back to storing it in a central database with the authentication still in the cookie. This is how Flickr approaches statelessness.

Tip #5: Test with cURL or rest-client

cURL makes it easy to execute any HTTP METHOD on a resource URL. You can pass request parameters and headers as well as inspect response headers and data. The command line tool ‘curl’ is standard on many *nix distros. Windows users should check out MinGW/MSYS which supports cURL. Even PHP has cURL functions which are enabled on most hosts (tp://us2.php.net/manual/en/curl.setup.php">php/curl install page).

cURL Example Usage & Common Parameters:

# curl -X PUT http://www.foo.com/bar/1 -d "some=var" -d "other=var2" -H "Accept: text/json" -I

-X [METHOD] Specify the HTTP method.
-d “name=value” Set a POST/PUT field name and value.
-H [HEADER] Set a header.
-I Only display response’s headers.

Alternatively, a free GUI to test REST interfaces is Java/Swing based rest-client. rest-client is scriptable and has support for JSON/XML.

Tip #6 - Use a RESTful PHP Framework

Frankly, developers shouldn’t have to worry about many of these low-level details of REST when writing PHP apps. REST is based on conventions and conventions, by nature, involve a lot of boilerplate. This was one of the motivating reasons for Recess, a REST PHP Framework. Recess has RESTful routing that makes creating friendly URLs that respond to PUT, POST, DELETE, & GET with ease. Check out this REST routing screencast for a preview of how it works or download Recess and have some fun!