Update README. Add default 500 error to Router. Close #3

This commit is contained in:
PJ Dietz 2013-05-27 12:59:55 -04:00
parent 8aae07660c
commit fafce16e9e
3 changed files with 69 additions and 33 deletions

View File

@ -4,7 +4,6 @@ WellRESTed
WellRESTed provides classes to help you create RESTful APIs and work with HTTP requests and responses. WellRESTed provides classes to help you create RESTful APIs and work with HTTP requests and responses.
Requirements Requirements
------------ ------------
@ -13,7 +12,6 @@ Requirements
- [PHP cURL](http://php.net/manual/en/book.curl.php) for making requests - [PHP cURL](http://php.net/manual/en/book.curl.php) for making requests
Install Install
------- -------
@ -27,15 +25,14 @@ Add an entry for "pjdietz/wellrested" to your composer.json file's **require** p
} }
``` ```
Use Composer to download and install WellRESTed. Run these commands from the directory containing the composer.json file. Use Composer to download and install WellRESTed. Run these commands from the directory containing the **composer.json** file.
```bash ```bash
$ curl -s https://getcomposer.org/installer | php $ curl -s https://getcomposer.org/installer | php
$ php composer.phar install $ php composer.phar install
``` ```
You can now use WellRESTed by including the autoload.php file generated by Composer. (vendor/autoload.php) You can now use WellRESTed by including the **autoload.php** file generated by Composer. (vendor/autoload.php)
Examples Examples
@ -43,7 +40,7 @@ Examples
### Routing ### Routing
WellRESTed's primary goal is to facilitate mapping of URIs to classes that will provide or accept representations. To do this, create a Router instance and load it up with some Routes. Each Route is simply a mapping of a URI pattern to a classname. The classname represents the Handler class which the router will dispatch at the time it recieves a request for the given URI. The handlers are never loaded until they are needed. WellRESTed's primary goal is to facilitate mapping of URIs to classes that will provide or accept representations. To do this, create a Router instance and load it up with some Routes. Each Route is simply a mapping of a URI pattern to a classname. The classname represents the Handler class which the router will dispatch at the time it recieves a request for the given URI. The handlers are never loaded unless they are needed.
Here's an example of a Router that will handle two URIs: Here's an example of a Router that will handle two URIs:
@ -58,11 +55,11 @@ $response = $router->getResponse();
$response->respond(); $response->respond();
``` ```
When you create your Handler subclass, you will provide a method for each HTTP verb you would like the endpoint to support. For example, if /things/ should support GET, you would override the get() method. For POST, post(), etc. When you create your Handler subclass, you will provide a method for each HTTP verb you would like the endpoint to support. For example, if **/things/** should support GET, you would override the get() method. For POST, post(), etc.
If your endpoint should reject particular verbs, no worries. The Handler base class defines the default verb-handling methods to respond with a 405 Method Not Allowed status. If your endpoint should reject particular verbs, no worries. The Handler base class defines the default verb-handling methods to respond with a 405 Method Not Allowed status.
Here's a simple Handler that matches the first endpoint, /things/. Here's a simple Handler that matches the first endpoint, **/things/**.
```php ```php
class ThingCollectionHandler extends \pjdietz\WellRESTed\Handler class ThingCollectionHandler extends \pjdietz\WellRESTed\Handler
@ -70,13 +67,13 @@ class ThingCollectionHandler extends \pjdietz\WellRESTed\Handler
protected function get() protected function get()
{ {
// Read some things from the database, cache, whatever. // Read some things from the database, cache, whatever.
// ... // ...read this into the variable $data
// Set the values for the instance's reponse member. This what the // Set the values for the instance's reponse member. This is what the
// Router will eventually use to output a response to the client. // Router will eventually use to output a response to the client.
$this->response->statusCode = 200; $this->response->setStatusCode(200);
$this->response->setHeader('Content-Type', 'application/json'); $this->response->setHeader('Content-Type', 'application/json');
$this->response->body = json_encode($data); $this->response->setBody(json_encode($data));
} }
protected function post() protected function post()
@ -85,46 +82,47 @@ class ThingCollectionHandler extends \pjdietz\WellRESTed\Handler
// ... // ...
// Build a reponse to send to the client. // Build a reponse to send to the client.
$this->response->statusCode = 201; $this->response->setStatusCode(201);
$this->response->body = 'You added a thing!'; $this->response->setBody('You added a thing!');
} }
} }
``` ```
This Handler works with the second endpoint, /things/{id}. The pattern for this endpoing has a variable in it ({id}). The Handler can access path variables through its args member, which is an associative array of variable from the URI. This Handler works with the second endpoint, **/things/{id}**. The pattern for this endpoing has the variable **{id}** in it. The Handler can access path variables through its **args** member, which is an associative array of variables from the URI.
```php ```php
class ThingItemHandler extends \pjdietz\WellRESTed\Handler class ThingItemHandler extends \pjdietz\WellRESTed\Handler
{ {
protected function get() protected function get()
{ {
// Lookup a Thing based on $this->args['id'] // Lookup a Thing ($thing) based on $this->args['id']
// ... // ...
if ($thing) { if ($thing) {
// The Thing exists! Let's output a representation. // The Thing exists! Let's output a representation.
$this->response->statusCode = 200; $this->response->setStatusCode(200);
$this->response->setHeader('Content-Type', 'application/json'); $this->response->setHeader('Content-Type', 'application/json');
$this->response->body = json_encode($thing); $this->response->setBody(json_encode($thing));
} else { } else {
// The ID did not match anything. // The ID did not match anything.
$this->response->statusCode = 404; $this->response->setStatusCode(404);
$this->response->setHeader('Content-Type', 'text/plain'); $this->response->setHeader('Content-Type', 'text/plain');
$this->response->body = 'No thing with id ' . $this->args['id']; $this->response->setBody('No thing with id ' . $this->args['id']);
} }
} }
} }
``` ```
### Requests and Responses ### Requests and Responses
You've already seen a Response in use in the examples above. You can also use Responses outside of Handlers. Let's take a look at creating a new Response, setting a headers, supplying the body, and outputting. You've already seen a Response in use in the examples above. You can also use Responses outside of Handlers. Let's take a look at creating a new Response, setting a header, supplying the body, and outputting.
```php ```php
$resp = new \pjdietz\WellRESTed\Response(); $resp = new \pjdietz\WellRESTed\Response();
$resp->statusCode = 200; $resp->setStatusCode(200);
$resp->setHeader('Content-type', 'text/plain'); $resp->setHeader('Content-type', 'text/plain');
$resp->body = 'Hello world!'; $resp->setBody('Hello world!');
$resp->respond(); $resp->respond();
exit; exit;
``` ```
@ -134,9 +132,10 @@ The Request class goes hand-in-hand with the Response class. Again, this is used
```php ```php
$rqst = \pjdietz\WellRESTed\Request::getRequest(); $rqst = \pjdietz\WellRESTed\Request::getRequest();
if ($rqst->method === 'PUT') { if ($rqst->getMethod() === 'PUT') {
$obj = json_decode($rqst->body); $obj = json_decode($rqst->getBody());
// Do something with the JSON sent as the message body. // Do something with the JSON sent as the message body.
// ...
} }
``` ```
@ -145,20 +144,50 @@ The Request class can also make a request to another server and provide the resp
```php ```php
// Prepare a request. // Prepare a request.
$rqst = new \pjdietz\WellRESTed\Request(); $rqst = new \pjdietz\WellRESTed\Request();
$rqst->uri = 'http://my.api.local/resources/'; $rqst->setUri('http://my.api.local/resources/');
$rqst->method = 'POST'; $rqst->setMethod('POST');
$rqst->body = json_encode($newResource); $rqst->setBody(json_encode($newResource));
// Make the request. // Make the request.
$resp = $rqst->request(); $resp = $rqst->request();
// Read the response. // Read the response.
if ($resp->statusCode === 201) { if ($resp->getStatusCode() === 201) {
// The new resource was created. // The new resource was created.
$newResource = json_decode($resp->body); $createdResource = json_decode($resp->getBody());
} }
``` ```
### Routing from a Handler
One more fun thing you can do with WellRESTed is use your API from inside your API. WellRESTed makes this easy becuase each dispatched Handler keeps a reference to the Router that dispatched it.
Suppose you have an endpoint that needs to look up information using another endpoint. Now that you've seen that you can create and work with your own requests and responses, we'll look at how to use them in the context of a handler.
```php
class NestedHandler extends \pjdietz\WellRESTed\Handler
{
protected function get()
{
// To build this response, we need some of the info from the /things/ representation.
// Create a Request instance to query this API.
$rqst = new Request();
$rqst->setPath('/things/');
// Use this handler's reference to the router that dispatched it.
$router = $this->getRouter();
// Pass the request for /things/ to the router to get a response.
$resp = $router->getResponse($rqst);
// Read the status code, headers, body, etc. on the response.
// ...
}
}
```
More Examples More Examples
--------------- ---------------

View File

@ -173,7 +173,7 @@ abstract class Handler implements HandlerInterface
$this->get(); $this->get();
if ($this->response->getStatusCode() == 200) { if ($this->response->getStatusCode() == 200) {
$this->response->setBody(''); $this->response->setBody('', false);
} }
} }

View File

@ -70,11 +70,10 @@ class Router implements RouterInterface
$handler->setRouter($this); $handler->setRouter($this);
return $handler->getResponse(); return $handler->getResponse();
} else { } else {
return $this->getNoRouteResponse($request); return $this->getInternalServerErrorResponse($request);
} }
} }
} }
return $this->getNoRouteResponse($request); return $this->getNoRouteResponse($request);
} }
@ -91,4 +90,12 @@ class Router implements RouterInterface
return $response; return $response;
} }
/** Prepare a response indicating a 500 Internal Server Error */
protected function getInternalServerErrorResponse(RequestInterface $request)
{
$response = new Response(500);
$response->body = 'Server error at ' . $request->getPath();
return $response;
}
} }