mirror of
https://github.com/codeigniter4/CodeIgniter4.git
synced 2025-02-20 11:44:28 +08:00
Method in place now to automatically format the response type based on content negotiation with the request. Started docs.
This commit is contained in:
parent
96b85c980c
commit
f2c6cb2e82
69
application/Config/API.php
Normal file
69
application/Config/API.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class API extends BaseConfig
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Available Response Formats
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When you perform content negotiation with the request, these are the
|
||||
| available formats that your application supports. This is currently
|
||||
| only used with the API\ResponseTrait. A valid Formatter must exist
|
||||
| for the specified format.
|
||||
|
|
||||
| These formats are only checked when the data passed to the respond()
|
||||
| method is an array.
|
||||
|
|
||||
*/
|
||||
public $supportedResponseFormats = [
|
||||
'application/json',
|
||||
'application/xml'
|
||||
];
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Formatters
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Lists the class to use to format responses with of a particular type.
|
||||
| For each mime type, list the class that should be used. Formatters
|
||||
| can be retrieved through the getFormatter() method.
|
||||
|
|
||||
*/
|
||||
public $formatters = [
|
||||
'application/json' => \CodeIgniter\API\JSONFormatter::class,
|
||||
'application/xml' => \CodeIgniter\API\XMLFormatter::class
|
||||
];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A Factory method to return the appropriate formatter for the given mime type.
|
||||
*
|
||||
* @param string $mime
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFormatter(string $mime)
|
||||
{
|
||||
if (! array_key_exists($mime, $this->formatters))
|
||||
{
|
||||
throw new \InvalidArgumentException('No Formatter defined for mime type: '. $mime);
|
||||
}
|
||||
|
||||
$class = $this->formatters[$mime];
|
||||
|
||||
if (! class_exists($class))
|
||||
{
|
||||
throw new \BadMethodCallException($class.' is not a valid Formatter.');
|
||||
}
|
||||
|
||||
return new $class();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
}
|
91
application/Controllers/Checks.php
Normal file
91
application/Controllers/Checks.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php namespace App\Controllers;
|
||||
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
use CodeIgniter\Controller;
|
||||
use Config\Database;
|
||||
|
||||
class Checks extends Controller
|
||||
{
|
||||
use ResponseTrait;
|
||||
|
||||
public function index()
|
||||
{
|
||||
session()->start();
|
||||
}
|
||||
|
||||
|
||||
public function escape()
|
||||
{
|
||||
$db = Database::connect();
|
||||
$db->initialize();
|
||||
|
||||
$jobs = $db->table('job')
|
||||
->whereNotIn('name', ['Politician', 'Accountant'])
|
||||
->get()
|
||||
->getResult();
|
||||
|
||||
die(var_dump($jobs));
|
||||
}
|
||||
|
||||
public function password()
|
||||
{
|
||||
$db = Database::connect();
|
||||
$db->initialize();
|
||||
|
||||
$result = $db->table('misc')
|
||||
->insert([
|
||||
'key' => 'password',
|
||||
'value' => '$2y$10$ErQlCj/Mo10il.FthAm0WOjYdf3chZEGPFqaPzjqOX2aj2uYf5Ihq'
|
||||
]);
|
||||
|
||||
die(var_dump($result));
|
||||
}
|
||||
|
||||
|
||||
public function forms()
|
||||
{
|
||||
helper('form');
|
||||
|
||||
var_dump(form_open());
|
||||
}
|
||||
|
||||
public function api()
|
||||
{
|
||||
$data = array(
|
||||
"total_users" => 3,
|
||||
"users" => array(
|
||||
array(
|
||||
"id" => 1,
|
||||
"name" => "Nitya",
|
||||
"address" => array(
|
||||
"country" => "India",
|
||||
"city" => "Kolkata",
|
||||
"zip" => 700102,
|
||||
)
|
||||
),
|
||||
array(
|
||||
"id" => 2,
|
||||
"name" => "John",
|
||||
"address" => array(
|
||||
"country" => "USA",
|
||||
"city" => "Newyork",
|
||||
"zip" => "NY1234",
|
||||
)
|
||||
),
|
||||
array(
|
||||
"id" => 3,
|
||||
"name" => "Viktor",
|
||||
"address" => array(
|
||||
"country" => "Australia",
|
||||
"city" => "Sydney",
|
||||
"zip" => 123456,
|
||||
)
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
return $this->respond($data);
|
||||
}
|
||||
|
||||
|
||||
}
|
13
system/API/FormatterInterface.php
Normal file
13
system/API/FormatterInterface.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php namespace CodeIgniter\API;
|
||||
|
||||
interface FormatterInterface
|
||||
{
|
||||
/**
|
||||
* Takes the given data and formats it.
|
||||
*
|
||||
* @param $data
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function format(array $data);
|
||||
}
|
49
system/API/JSONFormatter.php
Normal file
49
system/API/JSONFormatter.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php namespace CodeIgniter\API;
|
||||
|
||||
class JSONFormatter implements FormatterInterface
|
||||
{
|
||||
/**
|
||||
* The error strings to use if encoding hits an error.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $errors = [
|
||||
JSON_ERROR_NONE => 'No error has occurred',
|
||||
JSON_ERROR_DEPTH => 'The maximum stack depth has been exceeded',
|
||||
JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
|
||||
JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded',
|
||||
JSON_ERROR_SYNTAX => 'Syntax error',
|
||||
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded',
|
||||
];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes the given data and formats it.
|
||||
*
|
||||
* @todo Handle converting entity classes from db results, since we handle loading/saving those...
|
||||
*
|
||||
* @param $data
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function format(array $data)
|
||||
{
|
||||
$options = ENVIRONMENT == 'production'
|
||||
? JSON_NUMERIC_CHECK
|
||||
: JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT;
|
||||
|
||||
$result = json_encode($data, 512, $options);
|
||||
|
||||
// If result is NULL, then an error happened.
|
||||
// Let them know.
|
||||
if ($result === null)
|
||||
{
|
||||
throw new \RuntimeException($this->errors[json_last_error()]);
|
||||
}
|
||||
|
||||
return utf8_encode($result);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
* consistent HTTP responses under a variety of common
|
||||
* situations when working as an API.
|
||||
*
|
||||
* @property $request CodeIgniter\HTTP\Request
|
||||
* @property $response CodeIgniter\HTTP\Response
|
||||
*
|
||||
* @package CodeIgniter\API
|
||||
@ -100,7 +101,7 @@ trait ResponseTrait
|
||||
{
|
||||
if (! is_array($messages))
|
||||
{
|
||||
$message = [$messages];
|
||||
$messages = [$messages];
|
||||
}
|
||||
|
||||
$response = [
|
||||
@ -279,9 +280,16 @@ trait ResponseTrait
|
||||
return $data;
|
||||
}
|
||||
|
||||
// @todo Implement a formatting library so we have other options
|
||||
$this->setContentType('json');
|
||||
return json_encode($data);
|
||||
$config = new \Config\API();
|
||||
|
||||
// Determine correct response type through content negotiation
|
||||
$format = $this->request->negotiate('media', $config->supportedResponseFormats);
|
||||
|
||||
$this->setContentType($format);
|
||||
|
||||
$formatter = $config->getFormatter($format);
|
||||
|
||||
return $formatter->format($data);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -296,14 +304,14 @@ trait ResponseTrait
|
||||
{
|
||||
switch ($type)
|
||||
{
|
||||
case 'html':
|
||||
$this->response->setContentType('text/html');
|
||||
case 'text/html':
|
||||
$this->response = $this->response->setContentType('text/html');
|
||||
break;
|
||||
case 'json':
|
||||
$this->response->setContentType('application/json');
|
||||
case 'application/json':
|
||||
$this->response = $this->response->setContentType('application/json');
|
||||
break;
|
||||
case 'xml':
|
||||
$this->response->setContentType('text/xml');
|
||||
case 'application/xml':
|
||||
$this->response = $this->response->setContentType('text/xml');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
64
system/API/XMLFormatter.php
Normal file
64
system/API/XMLFormatter.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php namespace CodeIgniter\API;
|
||||
|
||||
class XMLFormatter implements FormatterInterface
|
||||
{
|
||||
/**
|
||||
* Takes the given data and formats it.
|
||||
*
|
||||
* @param $data
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function format(array $data)
|
||||
{
|
||||
$result = null;
|
||||
|
||||
// SimpleXML is installed but default
|
||||
// but best to check, and then provide a fallback.
|
||||
if (! extension_loaded('simplexml'))
|
||||
{
|
||||
throw new \RuntimeException('The SimpleXML extension is required to format XML.');
|
||||
}
|
||||
|
||||
$output = new \SimpleXMLElement("<?xml version=\"1.0\"?><response></response>");
|
||||
|
||||
$this->arrayToXML($data, $output);
|
||||
|
||||
return $output->asXML();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A recursive method to convert an array into a valid XML string.
|
||||
*
|
||||
* Written by CodexWorld. NOTE: waiting on confirmation if we can use....
|
||||
* @see http://www.codexworld.com/convert-array-to-xml-in-php/
|
||||
*
|
||||
* @param array $data
|
||||
* @param $output
|
||||
*/
|
||||
protected function arrayToXML(array $data, &$output)
|
||||
{
|
||||
foreach ($data as $key => $value)
|
||||
{
|
||||
if (is_array($value))
|
||||
{
|
||||
if (! is_numeric($key))
|
||||
{
|
||||
$subnode = $output->addChild("$key");
|
||||
$this->arrayToXML($value, $subnode);
|
||||
} else
|
||||
{
|
||||
$subnode = $output->addChild("item{$key}");
|
||||
$this->arrayToXML($value, $subnode);
|
||||
}
|
||||
} else
|
||||
{
|
||||
$output->addChild("$key", htmlspecialchars("$value"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
98
user_guide_src/source/libraries/api_responses.rst
Normal file
98
user_guide_src/source/libraries/api_responses.rst
Normal file
@ -0,0 +1,98 @@
|
||||
##################
|
||||
API Response Trait
|
||||
##################
|
||||
|
||||
Much of modern PHP development requires building API's, whether simply to provide data for a javascript-heavy
|
||||
single page application, or as a standalone product. CodeIgniter provides an API Response trait that can be
|
||||
used with any controller to make common response types simple, with no need to remember which HTTP status code
|
||||
should be returned for which response types.
|
||||
|
||||
.. contents:: Page Contents
|
||||
:local:
|
||||
|
||||
*************
|
||||
Example Usage
|
||||
*************
|
||||
|
||||
The following example shows a common usage pattern within your controllers.
|
||||
|
||||
::
|
||||
|
||||
<?php namespace App\Controllers;
|
||||
|
||||
class Users extends \CodeIgniter\Controller
|
||||
{
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
|
||||
public function createUser()
|
||||
{
|
||||
$model = new UserModel();
|
||||
$user = $model->save($this->request->getPost());
|
||||
|
||||
// Respond with 201 status code
|
||||
return $this->respondCreated();
|
||||
}
|
||||
}
|
||||
|
||||
In this example, an HTTP status code of 201 is returned, with the generic status message, 'Created'. Methods
|
||||
exist for the most common use cases::
|
||||
|
||||
// Generic response method
|
||||
respond($data, 200);
|
||||
// Generic failure response
|
||||
fail($errors, 400);
|
||||
// Item created response
|
||||
respondCreated($data);
|
||||
// Item successfully deleted
|
||||
respondDeleted($data);
|
||||
// Client isn't authorized
|
||||
failUnauthorized($description);
|
||||
// Forbidden action
|
||||
failForbidden($description);
|
||||
// Resource Not Found
|
||||
failNotFound($description);
|
||||
// Data did not validate
|
||||
failValidationError($description);
|
||||
// Resource already exists
|
||||
failResourceExists($description);
|
||||
// Resource previously deleted
|
||||
failResourceGone($description);
|
||||
// Client made too many requests
|
||||
failTooManyRequests($description);
|
||||
|
||||
***********************
|
||||
Handling Response Types
|
||||
***********************
|
||||
|
||||
When you pass your data in any of these methods, they will determine the data type to format the results as based on
|
||||
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 nothing else has been specified within Config\API.php, the ``$supportedResponseFormats`` property.
|
||||
|
||||
|
||||
|
||||
===============
|
||||
Class Reference
|
||||
===============
|
||||
|
||||
.. php:method:: respond($data[, $statusCode=200[, $message='']])
|
||||
|
||||
:param mixed $data: The data to return to the client. Either string or array.
|
||||
:param int $statusCode: The HTTP status code to return. Defaults to 200
|
||||
:param string $message: A custom "reason" message to return.
|
||||
|
||||
This is the method used by all other methods in this trait to return a response to the client.
|
||||
|
||||
The ``$data`` element can be either a string or an array. By default, a string will be returned as HTML,
|
||||
while an array will be run through json_encode and returned as JSON, unless :doc:`Content Negotiation </libraries/content_negotiation>`
|
||||
determines it should be returned in a different format.
|
||||
|
||||
If a ``$message`` string is passed, it will be used in place of the standard IANA reason codes for the
|
||||
response status. Not every client will respect the custom codes, though, and will use the IANA standards
|
||||
that match the status code.
|
||||
|
||||
.. note:: Since it sets the status code and body on the active Response instance, this should always
|
||||
be the final method in the script execution.
|
||||
|
@ -5,6 +5,7 @@ Library Reference
|
||||
.. toctree::
|
||||
:titlesonly:
|
||||
|
||||
api_responses
|
||||
benchmark
|
||||
caching
|
||||
cli
|
||||
|
Loading…
x
Reference in New Issue
Block a user