From c3f40655cd6f441cd6286b54ed0d4ad565b6f521 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Wed, 29 Apr 2020 00:39:17 -0500 Subject: [PATCH] Allow bypassing content negotiation during API responses. --- system/API/ResponseTrait.php | 28 +++++++++++++------ tests/system/API/ResponseTraitTest.php | 15 ++++++++++ .../source/outgoing/api_responses.rst | 18 ++++++++++-- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/system/API/ResponseTrait.php b/system/API/ResponseTrait.php index 6ae80f3368..49248eb198 100644 --- a/system/API/ResponseTrait.php +++ b/system/API/ResponseTrait.php @@ -56,7 +56,6 @@ use Config\Format; */ trait ResponseTrait { - /** * Allows child classes to override the * status code that is used in their API. @@ -95,8 +94,11 @@ trait ResponseTrait ]; /** + * How to format the response data. + * Either 'json' or 'xml'. If blank will be + * determine through content negotiation. * - * @var string the representation format to return resource data in (json/xml) + * @var string */ protected $format = 'json'; @@ -384,17 +386,14 @@ trait ResponseTrait return $data; } - // Determine correct response type through content negotiation $config = new Format(); + $format = "application/$this->format"; - if (! in_array($this->format, ['json', 'xml'])) + // Determine correct response type through content negotiation if not explicitly declared + if (empty($this->format) || ! in_array($this->format, ['json', 'xml'])) { $format = $this->request->negotiate('media', $config->supportedResponseFormats, false); } - else - { - $format = "application/$this->format"; - } $this->response->setContentType($format); @@ -415,4 +414,17 @@ trait ResponseTrait return $this->formatter->format($data); } + /** + * Sets the format the response should be in. + * + * @param string $format + * + * @return $this + */ + public function setResponseFormat(string $format = null) + { + $this->format = strtolower($format); + + return $this; + } } diff --git a/tests/system/API/ResponseTraitTest.php b/tests/system/API/ResponseTraitTest.php index f1190ec645..580ca176b5 100644 --- a/tests/system/API/ResponseTraitTest.php +++ b/tests/system/API/ResponseTraitTest.php @@ -502,4 +502,19 @@ EOH; $this->assertStringStartsWith(config('Format')->supportedResponseFormats[0], $response->getHeaderLine('Content-Type')); } + public function testResponseFormat() + { + $data = ['foo' => 'something']; + + $controller = $this->makeController(); + $controller->setResponseFormat('json'); + $controller->respond($data, 201); + + $this->assertStringStartsWith('application/json', $this->response->getHeaderLine('Content-Type')); + + $controller->responseFormat('xml'); + $controller->respond($data, 201); + + $this->assertStringStartsWith('application/xml', $this->response->getHeaderLine('Content-Type')); + } } diff --git a/user_guide_src/source/outgoing/api_responses.rst b/user_guide_src/source/outgoing/api_responses.rst index 2a177cf612..e25e7cfbf4 100644 --- a/user_guide_src/source/outgoing/api_responses.rst +++ b/user_guide_src/source/outgoing/api_responses.rst @@ -73,7 +73,8 @@ When you pass your data in any of these methods, they will determine the data ty the following criteria: * If $data is a string, it will be treated as HTML to send back to the client. -* If $data is an array, it will try to negotiate the content type with what the client asked for, defaulting to JSON +* If $data is an array, it will be formatted according to the controller's ``$this->format`` value. If that is empty + it will try to negotiate the content type with what the client asked for, defaulting to JSON if nothing else has been specified within Config\API.php, the ``$supportedResponseFormats`` property. To define the formatter that is used, edit **Config/Format.php**. The ``$supportedResponseFormats`` contains a list of @@ -104,6 +105,17 @@ JSON data will be sent back to the client. Class Reference *************** +.. php:method:: setResponseFormat($format) + + :param string $format The type of response to return, either ``json`` or ``xml`` + + This defines the format to be used when formatting arrays in responses. If you provide a ``null`` value for + ``$format``, it will be automatically determined through content negotiation. + +:: + + return $this->setResponseFormat('json')->respond(['error' => false]); + .. php:method:: respond($data[, $statusCode=200[, $message='']]) @@ -186,13 +198,13 @@ Class Reference :param string $message: A custom "reason" message to return. :returns: The value of the Response object's send() method. - Sets the appropriate status code to use when a command was successfully executed by the server but there is no + Sets the appropriate status code to use when a command was successfully executed by the server but there is no meaningful reply to send back to the client, typically 204. :: sleep(1); - return $this->respondNoContent(); + return $this->respondNoContent(); .. php:method:: failUnauthorized(string $description = 'Unauthorized'[, string $code=null[, string $message = '']])