diff --git a/README.md b/README.md
index 969cf17..e4fdf43 100644
--- a/README.md
+++ b/README.md
@@ -53,10 +53,10 @@ WellRESTed's primary goal is to facilitate mapping of URIs to classes that will
// Build the router.
$myRouter = new Router();
$myRouter->addRoutes(array(
- new StaticRoute("/", "\\myapi\\Handlers\\RootHandler")),
- new StaticRoute("/cats/", "\\myapi\\Handlers\\CatCollectionHandler")),
- new TemplateRoute("/cats/{id}/", "\\myapi\\Handlers\\CatItemHandler"))
-);
+ new StaticRoute("/", "\\myapi\\Handlers\\RootHandler"),
+ new StaticRoute("/cats/", "\\myapi\\Handlers\\CatCollectionHandler"),
+ new TemplateRoute("/cats/{id}/", "\\myapi\\Handlers\\CatItemHandler")
+));
$myRouter->respond();
```
diff --git a/src/pjdietz/WellRESTed/Handler.php b/src/pjdietz/WellRESTed/Handler.php
index a1a81af..408c400 100644
--- a/src/pjdietz/WellRESTed/Handler.php
+++ b/src/pjdietz/WellRESTed/Handler.php
@@ -10,7 +10,6 @@
namespace pjdietz\WellRESTed;
-use pjdietz\WellRESTed\Exceptions\HttpExceptions\HttpException;
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
use pjdietz\WellRESTed\Interfaces\RequestInterface;
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
@@ -46,12 +45,7 @@ abstract class Handler implements HandlerInterface
$this->request = $request;
$this->args = $args;
$this->response = new Response();
- try {
- $this->buildResponse();
- } catch (HttpException $e) {
- $this->response->setStatusCode($e->getCode());
- $this->response->setBody($e->getMessage());
- }
+ $this->buildResponse();
return $this->response;
}
diff --git a/src/pjdietz/WellRESTed/RouteBuilder.php b/src/pjdietz/WellRESTed/RouteBuilder.php
index 09b2e66..f449811 100644
--- a/src/pjdietz/WellRESTed/RouteBuilder.php
+++ b/src/pjdietz/WellRESTed/RouteBuilder.php
@@ -83,6 +83,8 @@ class RouteBuilder
* ->variablePattern is passed to setDefaultVariablePattern()
*
* ->vars is passed to setTemplateVars()
+ *
+ * @param object
*/
public function readConfiguration($data)
{
diff --git a/src/pjdietz/WellRESTed/Router.php b/src/pjdietz/WellRESTed/Router.php
index 34bd164..6cf9f54 100644
--- a/src/pjdietz/WellRESTed/Router.php
+++ b/src/pjdietz/WellRESTed/Router.php
@@ -10,6 +10,7 @@
namespace pjdietz\WellRESTed;
+use pjdietz\WellRESTed\Exceptions\HttpExceptions\HttpException;
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
use pjdietz\WellRESTed\Interfaces\RequestInterface;
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
@@ -23,11 +24,14 @@ class Router implements HandlerInterface
{
/** @var array Array of Route objects */
private $routes;
+ /** @var array Hash array of status code => qualified HandlerInterface names for error handling. */
+ private $errorHandlers;
/** Create a new Router. */
public function __construct()
{
$this->routes = array();
+ $this->errorHandlers = array();
}
/**
@@ -41,9 +45,27 @@ class Router implements HandlerInterface
{
foreach ($this->routes as $route) {
/** @var HandlerInterface $route */
- $responce = $route->getResponse($request, $args);
- if ($responce) {
- return $responce;
+ try {
+ $response = $route->getResponse($request, $args);
+ } catch (HttpException $e) {
+ $response = new Response();
+ $response->setStatusCode($e->getCode());
+ $response->setBody($e->getMessage());
+ }
+ if ($response) {
+ // Check if the router has an error handler for this status code.
+ $status = $response->getStatusCode();
+ if (array_key_exists($status, $this->errorHandlers)) {
+ /** @var HandlerInterface $errorHandler */
+ $errorHandler = new $this->errorHandlers[$status]();
+ // Pass the response triggering this along to the error handler.
+ $errorArgs = array("response" => $response);
+ if ($args) {
+ $errorArgs = array_merge($args, $errorArgs);
+ }
+ return $errorHandler->getResponse($request, $errorArgs);
+ }
+ return $response;
}
}
return null;
@@ -73,10 +95,34 @@ class Router implements HandlerInterface
}
}
+ /**
+ * Add a custom error handler.
+ *
+ * @param integer $statusCode The error status code.
+ * @param string $errorHandler Fully qualified name to an autoloadable handler class.
+ */
+ public function setErrorHandler($statusCode, $errorHandler)
+ {
+ $this->errorHandlers[$statusCode] = $errorHandler;
+ }
+
+ /**
+ * Add custom error handlers.
+ *
+ * @param array $errorHandlers Array mapping integer error codes to qualified handler names.
+ */
+ public function setErrorHandlers(array $errorHandlers)
+ {
+ foreach ($errorHandlers as $statusCode => $errorHandler) {
+ $this->setErrorHandler($statusCode, $errorHandler);
+ }
+ }
+
/**
* Dispatch the singleton Request through the router and output the response.
*
* Respond with a 404 Not Found if no route provides a response.
+ * @param array|null $args
*/
public function respond($args = null)
{
@@ -89,7 +135,7 @@ class Router implements HandlerInterface
}
/**
- * Prepare a resonse indicating a 404 Not Found error
+ * Prepare a response indicating a 404 Not Found error
*
* @param RequestInterface $request
* @return ResponseInterface
diff --git a/test/HandlerTest.php b/test/HandlerTest.php
index ca0f732..5c55ba6 100644
--- a/test/HandlerTest.php
+++ b/test/HandlerTest.php
@@ -2,7 +2,6 @@
namespace pjdietz\WellRESTed\Test;
-use pjdietz\WellRESTed\Exceptions\HttpExceptions\NotFoundException;
use pjdietz\WellRESTed\Handler;
class HandlerTest extends \PHPUnit_Framework_TestCase
@@ -44,18 +43,6 @@ class HandlerTest extends \PHPUnit_Framework_TestCase
];
}
- public function testTranslateHttpExceptionToResponse()
- {
- $mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
- $mockRequest->expects($this->any())
- ->method('getMethod')
- ->will($this->returnValue("GET"));
-
- $handler = new ExceptionHandler();
- $resp = $handler->getResponse($mockRequest);
- $this->assertEquals(404, $resp->getStatusCode());
- }
-
public function testReadAllowedMethods()
{
$mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
@@ -71,14 +58,6 @@ class HandlerTest extends \PHPUnit_Framework_TestCase
}
-class ExceptionHandler extends Handler
-{
- protected function get()
- {
- throw new NotFoundException();
- }
-}
-
class OptionsHandler extends Handler
{
protected function getAllowedMethods()
diff --git a/test/RouterTest.php b/test/RouterTest.php
index 5278136..afd9c0a 100644
--- a/test/RouterTest.php
+++ b/test/RouterTest.php
@@ -2,8 +2,10 @@
namespace pjdietz\WellRESTed\Test;
+use pjdietz\WellRESTed\Exceptions\HttpExceptions\ForbiddenException;
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
use pjdietz\WellRESTed\Interfaces\RequestInterface;
+use pjdietz\WellRESTed\Interfaces\ResponseInterface;
use pjdietz\WellRESTed\Response;
use pjdietz\WellRESTed\Router;
use pjdietz\WellRESTed\Routes\StaticRoute;
@@ -46,6 +48,75 @@ class RouterTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(200, $resp->getStatusCode());
}
+ public function testRespondWithDefaultErrorForException()
+ {
+ $mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
+ $mockRequest->expects($this->any())
+ ->method('getPath')
+ ->will($this->returnValue("/"));
+
+ $router = new Router();
+ $router->addRoute(new StaticRoute("/", __NAMESPACE__ . '\\ForbiddenExceptionHandler'));
+ $resp = $router->getResponse($mockRequest);
+ $this->assertEquals(403, $resp->getStatusCode());
+ }
+
+ public function testRespondWithErrorHandlerForException()
+ {
+ $mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
+ $mockRequest->expects($this->any())
+ ->method('getPath')
+ ->will($this->returnValue("/"));
+
+ $router = new Router();
+ $router->addRoute(new StaticRoute("/", __NAMESPACE__ . '\\ForbiddenExceptionHandler'));
+ $router->setErrorHandler(403, __NAMESPACE__ . '\\ForbiddenErrorHandler');
+ $resp = $router->getResponse($mockRequest);
+ $this->assertEquals("YOU SHALL NOT PASS!", $resp->getBody());
+ }
+
+ public function testRespondWithErrorHandlerForStatusCode()
+ {
+ $mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
+ $mockRequest->expects($this->any())
+ ->method('getPath')
+ ->will($this->returnValue("/"));
+
+ $router = new Router();
+ $router->addRoute(new StaticRoute("/", __NAMESPACE__ . '\\ForbiddenHandler'));
+ $router->setErrorHandler(403, __NAMESPACE__ . '\\ForbiddenErrorHandler');
+ $resp = $router->getResponse($mockRequest);
+ $this->assertEquals("YOU SHALL NOT PASS!", $resp->getBody());
+ }
+
+ public function testRespondWithErrorHandlerUsingOriginalResponse()
+ {
+ $mockRequest = $this->getMock('\pjdietz\WellRESTed\Interfaces\RequestInterface');
+ $mockRequest->expects($this->any())
+ ->method('getPath')
+ ->will($this->returnValue("/"));
+
+ $router = new Router();
+ $router->addRoute(new StaticRoute("/", __NAMESPACE__ . '\\MessageHandler'));
+ $router->setErrorHandlers([404 => __NAMESPACE__ . '\\MessageErrorHandler']);
+ $resp = $router->getResponse($mockRequest);
+ $this->assertEquals("