mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-30 22:48:19 +08:00
Added XmlResponseFormatter.
This commit is contained in:
@@ -132,7 +132,7 @@ class Controller extends Component
|
|||||||
* the route will start from the application; otherwise, it will start from the parent module of this controller.
|
* the route will start from the application; otherwise, it will start from the parent module of this controller.
|
||||||
* @param string $route the route to be handled, e.g., 'view', 'comment/view', '/admin/comment/view'.
|
* @param string $route the route to be handled, e.g., 'view', 'comment/view', '/admin/comment/view'.
|
||||||
* @param array $params the parameters to be passed to the action.
|
* @param array $params the parameters to be passed to the action.
|
||||||
* @return integer the status code returned by the action execution. 0 means normal, and other values mean abnormal.
|
* @return mixed the result of the action
|
||||||
* @see runAction
|
* @see runAction
|
||||||
* @see forward
|
* @see forward
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ class Response extends \yii\base\Response
|
|||||||
* @var string the response format.
|
* @var string the response format.
|
||||||
*/
|
*/
|
||||||
public $format = self::FORMAT_HTML;
|
public $format = self::FORMAT_HTML;
|
||||||
|
/**
|
||||||
|
* @var array the formatters for converting data into the response content of the specified [[format]].
|
||||||
|
* The array keys are the format names, and the array values are the corresponding configurations
|
||||||
|
* for creating the formatter objects.
|
||||||
|
*/
|
||||||
|
public $formatters;
|
||||||
/**
|
/**
|
||||||
* @var string the charset of the text response. If not set, it will use
|
* @var string the charset of the text response. If not set, it will use
|
||||||
* the value of [[Application::charset]].
|
* the value of [[Application::charset]].
|
||||||
@@ -134,7 +140,9 @@ class Response extends \yii\base\Response
|
|||||||
*/
|
*/
|
||||||
private $_headers;
|
private $_headers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes this component.
|
||||||
|
*/
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
if ($this->version === null) {
|
if ($this->version === null) {
|
||||||
@@ -157,6 +165,13 @@ class Response extends \yii\base\Response
|
|||||||
return $this->_statusCode;
|
return $this->_statusCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the response status code.
|
||||||
|
* This method will set the corresponding status text if `$text` is null.
|
||||||
|
* @param integer $value the status code
|
||||||
|
* @param string $text the status text. If not set, it will be set automatically based on the status code.
|
||||||
|
* @throws InvalidParamException if the status code is invalid.
|
||||||
|
*/
|
||||||
public function setStatusCode($value, $text = null)
|
public function setStatusCode($value, $text = null)
|
||||||
{
|
{
|
||||||
$this->_statusCode = (int)$value;
|
$this->_statusCode = (int)$value;
|
||||||
@@ -194,9 +209,13 @@ class Response extends \yii\base\Response
|
|||||||
$this->clear();
|
$this->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the headers, cookies, content, status code of the response.
|
||||||
|
*/
|
||||||
public function clear()
|
public function clear()
|
||||||
{
|
{
|
||||||
$this->_headers = null;
|
$this->_headers = null;
|
||||||
|
$this->_cookies = null;
|
||||||
$this->_statusCode = null;
|
$this->_statusCode = null;
|
||||||
$this->_content = null;
|
$this->_content = null;
|
||||||
$this->statusText = null;
|
$this->statusText = null;
|
||||||
@@ -643,8 +662,9 @@ class Response extends \yii\base\Response
|
|||||||
* The existing content will be overwritten.
|
* The existing content will be overwritten.
|
||||||
* Depending on the value of [[format]], the data will be properly formatted.
|
* Depending on the value of [[format]], the data will be properly formatted.
|
||||||
* @param mixed $data the data that needs to be converted into the response content.
|
* @param mixed $data the data that needs to be converted into the response content.
|
||||||
* @param string $format the format of the response. The following formats are
|
* @param string $format the format of the response. The [[formatters]] property specifies
|
||||||
* supported by the default implementation:
|
* the supported formats and the corresponding formatters. Additionally, the following formats are
|
||||||
|
* supported if they are not found in [[formatters]]:
|
||||||
*
|
*
|
||||||
* - [[FORMAT_RAW]]: the data will be treated as the response content without any conversion.
|
* - [[FORMAT_RAW]]: the data will be treated as the response content without any conversion.
|
||||||
* No extra HTTP header will be added.
|
* No extra HTTP header will be added.
|
||||||
@@ -656,8 +676,8 @@ class Response extends \yii\base\Response
|
|||||||
* header will be set as "text/javascript". Note that in this case `$data` must be an array
|
* header will be set as "text/javascript". Note that in this case `$data` must be an array
|
||||||
* with "data" and "callback" elements. The former refers to the actual data to be sent,
|
* with "data" and "callback" elements. The former refers to the actual data to be sent,
|
||||||
* while the latter refers to the name of the JavaScript callback.
|
* while the latter refers to the name of the JavaScript callback.
|
||||||
* - [[FORMAT_XML]]: the data will be converted into XML format, and the "Content-Type"
|
* - [[FORMAT_XML]]: the data will be converted into XML format. Please refer to [[XmlResponseFormatter]]
|
||||||
* header will be set as "application/xml" if no previous "Content-Type" header is set.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
public function setContent($data, $format = null)
|
public function setContent($data, $format = null)
|
||||||
{
|
{
|
||||||
@@ -667,8 +687,28 @@ class Response extends \yii\base\Response
|
|||||||
$this->_content = $this->formatContent($data, $format);
|
$this->_content = $this->formatContent($data, $format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the given data as the specified format.
|
||||||
|
* @param mixed $data the data to be formatted.
|
||||||
|
* @param string $format the format to use.
|
||||||
|
* @return string the formatting result.
|
||||||
|
* @throws InvalidParamException if `$format` is not supported
|
||||||
|
* @throws InvalidConfigException if the formatter for the specified format is invalid
|
||||||
|
*/
|
||||||
protected function formatContent($data, $format)
|
protected function formatContent($data, $format)
|
||||||
{
|
{
|
||||||
|
if (isset($this->formatters[$format])) {
|
||||||
|
$formatter = $this->formatters[$format];
|
||||||
|
if (!is_object($formatter)) {
|
||||||
|
$formatter = Yii::createObject($formatter);
|
||||||
|
}
|
||||||
|
if ($formatter instanceof ResponseFormatter) {
|
||||||
|
return $formatter->format($this, $data);
|
||||||
|
} else {
|
||||||
|
throw new InvalidConfigException("The '$format' response formatter is invalid. It must implement the ResponseFormatter interface.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch ($this->format) {
|
switch ($this->format) {
|
||||||
case self::FORMAT_RAW:
|
case self::FORMAT_RAW:
|
||||||
return $data;
|
return $data;
|
||||||
@@ -686,7 +726,7 @@ class Response extends \yii\base\Response
|
|||||||
throw new InvalidParamException("The 'jsonp' response requires that the data be an array consisting of both 'data' and 'callback' elements.");
|
throw new InvalidParamException("The 'jsonp' response requires that the data be an array consisting of both 'data' and 'callback' elements.");
|
||||||
}
|
}
|
||||||
case self::FORMAT_XML:
|
case self::FORMAT_XML:
|
||||||
// todo
|
return Yii::createObject('yii\web\XmlResponseFormatter')->format($this, $data);
|
||||||
default:
|
default:
|
||||||
throw new InvalidConfigException("Unsupported response format: $format");
|
throw new InvalidConfigException("Unsupported response format: $format");
|
||||||
}
|
}
|
||||||
|
|||||||
25
framework/yii/web/ResponseFormatter.php
Normal file
25
framework/yii/web/ResponseFormatter.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace yii\web;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ResponseFormatter specifies the interface needed to format data for a Web response object.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
interface ResponseFormatter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Formats the given data for the response.
|
||||||
|
* @param Response $response the response object that will accept the formatted result
|
||||||
|
* @param mixed $data the data to be formatted
|
||||||
|
* @return string the formatted result
|
||||||
|
*/
|
||||||
|
function format($response, $data);
|
||||||
|
}
|
||||||
99
framework/yii/web/XmlResponseFormatter.php
Normal file
99
framework/yii/web/XmlResponseFormatter.php
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace yii\web;
|
||||||
|
|
||||||
|
use DOMDocument;
|
||||||
|
use DOMElement;
|
||||||
|
use DOMText;
|
||||||
|
use yii\base\Arrayable;
|
||||||
|
use yii\base\Component;
|
||||||
|
use yii\helpers\StringHelper;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XmlResponseFormatter formats the given data into an XML response content.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
class XmlResponseFormatter extends Component implements ResponseFormatter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the Content-Type header for the response
|
||||||
|
*/
|
||||||
|
public $contentType = 'application/xml';
|
||||||
|
/**
|
||||||
|
* @var string the XML version
|
||||||
|
*/
|
||||||
|
public $version = '1.0';
|
||||||
|
/**
|
||||||
|
* @var string the XML encoding. If not set, it will use the value of [[Response::charset]].
|
||||||
|
*/
|
||||||
|
public $encoding;
|
||||||
|
/**
|
||||||
|
* @var string the name of the root element.
|
||||||
|
*/
|
||||||
|
public $rootTag = 'response';
|
||||||
|
/**
|
||||||
|
* @var string the name of the elements that represent the array elements with numeric keys.
|
||||||
|
*/
|
||||||
|
public $itemTag = 'item';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the given data for the response.
|
||||||
|
* @param Response $response the response object that will accept the formatted result
|
||||||
|
* @param mixed $data the data to be formatted
|
||||||
|
* @return string the formatted result
|
||||||
|
*/
|
||||||
|
public function format($response, $data)
|
||||||
|
{
|
||||||
|
$response->getHeaders()->set('Content-Type', $this->contentType);
|
||||||
|
$dom = new DOMDocument($this->version, $this->encoding === null ? $response->charset : $this->encoding);
|
||||||
|
$root = new DOMElement($this->rootTag);
|
||||||
|
$dom->appendChild($root);
|
||||||
|
$this->buildXml($root, $data);
|
||||||
|
return $dom->saveXML();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DOMElement $element
|
||||||
|
* @param mixed $data
|
||||||
|
*/
|
||||||
|
protected function buildXml($element, $data)
|
||||||
|
{
|
||||||
|
if (is_object($data)) {
|
||||||
|
$child = new DOMElement(StringHelper::basename(get_class($data)));
|
||||||
|
$element->appendChild($child);
|
||||||
|
if ($data instanceof Arrayable) {
|
||||||
|
$this->buildXml($child, $data->toArray());
|
||||||
|
} else {
|
||||||
|
$array = array();
|
||||||
|
foreach ($data as $name => $value) {
|
||||||
|
$array[$name] = $value;
|
||||||
|
}
|
||||||
|
$this->buildXml($child, $array);
|
||||||
|
}
|
||||||
|
} elseif (is_array($data)) {
|
||||||
|
foreach ($data as $name => $value) {
|
||||||
|
if (is_int($name) && is_object($value)) {
|
||||||
|
$this->buildXml($element, $value);
|
||||||
|
} elseif (is_array($value) || is_object($value)) {
|
||||||
|
$child = new DOMElement(is_int($name) ? $this->itemTag : $name);
|
||||||
|
$element->appendChild($child);
|
||||||
|
$this->buildXml($child, $value);
|
||||||
|
} else {
|
||||||
|
$child = new DOMElement(is_int($name) ? $this->itemTag : $name);
|
||||||
|
$element->appendChild($child);
|
||||||
|
$child->appendChild(new DOMText((string)$value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$element->appendChild(new DOMText((string)$data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
117
tests/unit/framework/web/XmlResponseFormatterTest.php
Normal file
117
tests/unit/framework/web/XmlResponseFormatterTest.php
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace yiiunit\framework\web;
|
||||||
|
|
||||||
|
use yii\base\Object;
|
||||||
|
use yii\web\Response;
|
||||||
|
use yii\web\XmlResponseFormatter;
|
||||||
|
|
||||||
|
class Post extends Object
|
||||||
|
{
|
||||||
|
public $id;
|
||||||
|
public $title;
|
||||||
|
|
||||||
|
public function __construct($id, $title)
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
$this->title = $title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
class XmlResponseFormatterTest extends \yiiunit\TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Response
|
||||||
|
*/
|
||||||
|
public $response;
|
||||||
|
/**
|
||||||
|
* @var XmlResponseFormatter
|
||||||
|
*/
|
||||||
|
public $formatter;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
$this->mockApplication();
|
||||||
|
$this->response = new Response;
|
||||||
|
$this->formatter = new XmlResponseFormatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormatScalars()
|
||||||
|
{
|
||||||
|
$head = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||||
|
|
||||||
|
$xml = $head . "<response></response>\n";
|
||||||
|
$this->assertEquals($xml, $this->formatter->format($this->response, null));
|
||||||
|
|
||||||
|
$xml = $head . "<response>1</response>\n";
|
||||||
|
$this->assertEquals($xml, $this->formatter->format($this->response, 1));
|
||||||
|
|
||||||
|
$xml = $head . "<response>abc</response>\n";
|
||||||
|
$this->assertEquals($xml, $this->formatter->format($this->response, 'abc'));
|
||||||
|
|
||||||
|
$xml = $head . "<response>1</response>\n";
|
||||||
|
$this->assertEquals($xml, $this->formatter->format($this->response, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormatArrays()
|
||||||
|
{
|
||||||
|
$head = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||||
|
|
||||||
|
$xml = $head . "<response/>\n";
|
||||||
|
$this->assertEquals($xml, $this->formatter->format($this->response, array()));
|
||||||
|
|
||||||
|
$xml = $head . "<response><item>1</item><item>abc</item></response>\n";
|
||||||
|
$this->assertEquals($xml, $this->formatter->format($this->response, array(1, 'abc')));
|
||||||
|
|
||||||
|
$xml = $head . "<response><a>1</a><b>abc</b></response>\n";
|
||||||
|
$this->assertEquals($xml, $this->formatter->format($this->response, array(
|
||||||
|
'a' => 1,
|
||||||
|
'b' => 'abc',
|
||||||
|
)));
|
||||||
|
|
||||||
|
$xml = $head . "<response><item>1</item><item>abc</item><item><item>2</item><item>def</item></item><item>1</item></response>\n";
|
||||||
|
$this->assertEquals($xml, $this->formatter->format($this->response, array(
|
||||||
|
1,
|
||||||
|
'abc',
|
||||||
|
array(2, 'def'),
|
||||||
|
true,
|
||||||
|
)));
|
||||||
|
|
||||||
|
$xml = $head . "<response><a>1</a><b>abc</b><c><item>2</item><item>def</item></c><item>1</item></response>\n";
|
||||||
|
$this->assertEquals($xml, $this->formatter->format($this->response, array(
|
||||||
|
'a' => 1,
|
||||||
|
'b' => 'abc',
|
||||||
|
'c' => array(2, 'def'),
|
||||||
|
true,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormatObjects()
|
||||||
|
{
|
||||||
|
$head = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||||
|
|
||||||
|
$xml = $head . "<response><Post><id>123</id><title>abc</title></Post></response>\n";
|
||||||
|
$this->assertEquals($xml, $this->formatter->format($this->response, new Post(123, 'abc')));
|
||||||
|
|
||||||
|
$xml = $head . "<response><Post><id>123</id><title>abc</title></Post><Post><id>456</id><title>def</title></Post></response>\n";
|
||||||
|
$this->assertEquals($xml, $this->formatter->format($this->response, array(
|
||||||
|
new Post(123, 'abc'),
|
||||||
|
new Post(456, 'def'),
|
||||||
|
)));
|
||||||
|
|
||||||
|
$xml = $head . "<response><Post><id>123</id><title>abc</title></Post><a><Post><id>456</id><title>def</title></Post></a></response>\n";
|
||||||
|
$this->assertEquals($xml, $this->formatter->format($this->response, array(
|
||||||
|
new Post(123, 'abc'),
|
||||||
|
'a' => new Post(456, 'def'),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user