From 4d5ded4e955a51b2d4033b9475760d4fcc5e5a41 Mon Sep 17 00:00:00 2001 From: cpenny Date: Wed, 7 Mar 2018 11:41:27 +1300 Subject: [PATCH] Added general Exception catch/response. Added extension points to all response methods. --- src/RestfulServer.php | 74 +++++++++++++++---- tests/unit/RestfulServerTest.php | 16 ++++ .../RestfulServerTestExceptionThrown.php | 52 +++++++++++++ 3 files changed, 126 insertions(+), 16 deletions(-) create mode 100644 tests/unit/Stubs/RestfulServerTestExceptionThrown.php diff --git a/src/RestfulServer.php b/src/RestfulServer.php index 8212daf..35a59c0 100644 --- a/src/RestfulServer.php +++ b/src/RestfulServer.php @@ -154,21 +154,25 @@ class RestfulServer extends Controller // authenticate through HTTP BasicAuth $this->member = $this->authenticate(); - // handle different HTTP verbs - if ($this->request->isGET() || $this->request->isHEAD()) { - return $this->getHandler($className, $id, $relation); - } + try { + // handle different HTTP verbs + if ($this->request->isGET() || $this->request->isHEAD()) { + return $this->getHandler($className, $id, $relation); + } - if ($this->request->isPOST()) { - return $this->postHandler($className, $id, $relation); - } + if ($this->request->isPOST()) { + return $this->postHandler($className, $id, $relation); + } - if ($this->request->isPUT()) { - return $this->putHandler($className, $id, $relation); - } + if ($this->request->isPUT()) { + return $this->putHandler($className, $id, $relation); + } - if ($this->request->isDELETE()) { - return $this->deleteHandler($className, $id, $relation); + if ($this->request->isDELETE()) { + return $this->deleteHandler($className, $id, $relation); + } + } catch (\Exception $e) { + return $this->exceptionThrown($this->getRequestDataFormatter($className), $e); } // if no HTTP verb matches, return error @@ -681,7 +685,11 @@ class RestfulServer extends Controller $this->getResponse()->setStatusCode(401); $this->getResponse()->addHeader('WWW-Authenticate', 'Basic realm="API Access"'); $this->getResponse()->addHeader('Content-Type', 'text/plain'); - return "You don't have access to this item through the API."; + + $reponse = "You don't have access to this item through the API."; + $this->extend(__FUNCTION__, $reponse); + + return $reponse; } /** @@ -692,7 +700,11 @@ class RestfulServer extends Controller // return a 404 $this->getResponse()->setStatusCode(404); $this->getResponse()->addHeader('Content-Type', 'text/plain'); - return "That object wasn't found"; + + $reponse = "That object wasn't found"; + $this->extend(__FUNCTION__, $reponse); + + return $reponse; } /** @@ -702,7 +714,11 @@ class RestfulServer extends Controller { $this->getResponse()->setStatusCode(405); $this->getResponse()->addHeader('Content-Type', 'text/plain'); - return "Method Not Allowed"; + + $reponse = "Method Not Allowed"; + $this->extend(__FUNCTION__, $reponse); + + return $reponse; } /** @@ -712,7 +728,11 @@ class RestfulServer extends Controller { $this->response->setStatusCode(415); // Unsupported Media Type $this->getResponse()->addHeader('Content-Type', 'text/plain'); - return "Unsupported Media Type"; + + $reponse = "Unsupported Media Type"; + $this->extend(__FUNCTION__, $reponse); + + return $reponse; } /** @@ -729,6 +749,28 @@ class RestfulServer extends Controller 'messages' => $result->getMessages(), ]; + $this->extend(__FUNCTION__, $response, $result); + + return $responseFormatter->convertArray($response); + } + + /** + * @param DataFormatter $responseFormatter + * @param \Exception $e + * @return string + */ + protected function exceptionThrown(DataFormatter $responseFormatter, \Exception $e) + { + $this->getResponse()->setStatusCode(500); + $this->getResponse()->addHeader('Content-Type', $responseFormatter->getOutputContentType()); + + $response = [ + 'type' => get_class($e), + 'message' => $e->getMessage(), + ]; + + $this->extend(__FUNCTION__, $response, $e); + return $responseFormatter->convertArray($response); } diff --git a/tests/unit/RestfulServerTest.php b/tests/unit/RestfulServerTest.php index c188f35..d04202a 100644 --- a/tests/unit/RestfulServerTest.php +++ b/tests/unit/RestfulServerTest.php @@ -3,6 +3,7 @@ namespace SilverStripe\RestfulServer\Tests; use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestComment; +use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestExceptionThrown; use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestSecretThing; use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestPage; use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestAuthor; @@ -38,6 +39,8 @@ class RestfulServerTest extends SapphireTest RestfulServerTestPage::class, RestfulServerTestAuthor::class, RestfulServerTestAuthorRating::class, + RestfulServerTestValidationFailure::class, + RestfulServerTestExceptionThrown::class, ]; protected function urlSafeClassname($classname) @@ -685,4 +688,17 @@ class RestfulServerTest extends SapphireTest $responseArr = Convert::xml2array($response->getBody()); $this->assertEquals('SilverStripe\\ORM\\ValidationException', $responseArr['type']); } + + public function testExceptionThrownWithPOST() + { + $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestExceptionThrown::class); + $url = "{$this->baseURI}/api/v1/$urlSafeClassname/"; + $data = [ + 'Content' => 'Test', + ]; + $response = Director::test($url, $data, null, 'POST'); + // Assumption: XML is default output + $responseArr = Convert::xml2array($response->getBody()); + $this->assertEquals(\Exception::class, $responseArr['type']); + } } diff --git a/tests/unit/Stubs/RestfulServerTestExceptionThrown.php b/tests/unit/Stubs/RestfulServerTestExceptionThrown.php new file mode 100644 index 0000000..a830d6a --- /dev/null +++ b/tests/unit/Stubs/RestfulServerTestExceptionThrown.php @@ -0,0 +1,52 @@ + 'Text', + 'Title' => 'Text', + ); + + public function onBeforeWrite() + { + parent::onBeforeWrite(); + + throw new \Exception('This is an exception test'); + } + + public function canView($member = null) + { + return true; + } + + public function canEdit($member = null) + { + return true; + } + + public function canDelete($member = null) + { + return true; + } + + public function canCreate($member = null, $context = array()) + { + return true; + } +}