diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index e13e3d7..21d0f52 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -295,6 +295,7 @@ Yii Framework 2 Change Log - New: Added `yii\web\GroupUrlRule` (qiangxue) - New: Added `yii\filters\RateLimiter` (qiangxue) - New: Added various authentication methods, including `HttpBasicAuth`, `HttpBearerAuth`, `QueryParamAuth`, and `CompositeAuth` (qiangxue) +- New: Added `HtmlResponseFormatter` and `JsonResponseFormatter` (qiangxue) 2.0.0-alpha, December 1, 2013 ----------------------------- diff --git a/framework/web/HtmlResponseFormatter.php b/framework/web/HtmlResponseFormatter.php new file mode 100644 index 0000000..623cab3 --- /dev/null +++ b/framework/web/HtmlResponseFormatter.php @@ -0,0 +1,39 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use yii\base\Component; + +/** + * HtmlResponseFormatter formats the given data into an HTML response content. + * + * It is used by [[Response]] to format response data. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class HtmlResponseFormatter extends Component implements ResponseFormatterInterface +{ + /** + * @var string the Content-Type header for the response + */ + public $contentType = 'text/html'; + + /** + * Formats the specified response. + * @param Response $response the response to be formatted. + */ + public function format($response) + { + if (stripos($this->contentType, 'charset') === false) { + $this->contentType .= '; charset=' . $response->charset; + } + $response->getHeaders()->set('Content-Type', $this->contentType); + $response->content = $response->data; + } +} diff --git a/framework/web/JsonResponseFormatter.php b/framework/web/JsonResponseFormatter.php new file mode 100644 index 0000000..bd47704 --- /dev/null +++ b/framework/web/JsonResponseFormatter.php @@ -0,0 +1,68 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use Yii; +use yii\base\Component; +use yii\helpers\Json; + +/** + * JsonResponseFormatter formats the given data into a JSON or JSONP response content. + * + * It is used by [[Response]] to format response data. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class JsonResponseFormatter extends Component implements ResponseFormatterInterface +{ + /** + * @var boolean whether to use JSONP response format. When this is true, the [[Response::data|response data]] + * must be an array consisting of `data` and `callback` members. The latter should be a JavaScript + * function name while the former will be passed to this function as a parameter. + */ + public $useJsonp = false; + + /** + * Formats the specified response. + * @param Response $response the response to be formatted. + */ + public function format($response) + { + if ($this->useJsonp) { + $this->formatJsonp($response); + } else { + $this->formatJson($response); + } + } + + /** + * Formats response data in JSON format. + * @param Response $response + */ + protected function formatJson($response) + { + $response->getHeaders()->set('Content-Type', 'application/json; charset=UTF-8'); + $response->content = Json::encode($response->data); + } + + /** + * Formats response data in JSONP format. + * @param Response $response + */ + protected function formatJsonp($response) + { + $response->getHeaders()->set('Content-Type', 'application/javascript; charset=UTF-8'); + if (is_array($response->data) && isset($response->data['data'], $response->data['callback'])) { + $response->content = sprintf('%s(%s);', $response->data['callback'], Json::encode($response->data['data'])); + } else { + $response->content = ''; + Yii::warning("The 'jsonp' response requires that the data be an array consisting of both 'data' and 'callback' elements.", __METHOD__); + } + } +} diff --git a/framework/web/Response.php b/framework/web/Response.php index 9b4232d..1dfc10d 100644 --- a/framework/web/Response.php +++ b/framework/web/Response.php @@ -12,7 +12,6 @@ use yii\base\InvalidConfigException; use yii\base\InvalidParamException; use yii\helpers\Url; use yii\helpers\FileHelper; -use yii\helpers\Json; use yii\helpers\Security; use yii\helpers\StringHelper; @@ -82,7 +81,8 @@ class Response extends \yii\base\Response /** * @var string the response format. This determines how to convert [[data]] into [[content]] - * when the latter is not set. By default, the following formats are supported: + * when the latter is not set. The value of this property must be one of the keys declared in the [[formatters] array. + * By default, the following formats are supported: * * - [[FORMAT_RAW]]: the data will be treated as the response content without any conversion. * No extra HTTP header will be added. @@ -102,12 +102,38 @@ class Response extends \yii\base\Response */ public $format = self::FORMAT_HTML; /** + * @var array a list of supported response formats. The keys are MIME types (e.g. `application/json`) + * while the values are the corresponding formats (e.g. `html`, `json`) which must be supported by [[formatters]]. + * When this property is set, a content type negotiation process will be conducted to determine + * the value of [[format]] and the corresponding [[mimeType]] and [[acceptParams]] values. + */ + public $supportedFormats; + /** + * @var string the MIME type (e.g. `application/json`) chosen for this response after content type negotiation. + * This property will be set by the content type negotiation process. + */ + public $mimeType; + /** + * @var array the parameters (e.g. `['q' => 1, 'version' => '1.0']`) for the MIME type chosen + * by the content type negotiation. This is a list of name-value pairs associated with [[mimeType]] + * from the ACCEPT HTTP header. This property will be set by the content type negotiation process. + */ + public $acceptParams; + /** * @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. * @see format */ - public $formatters; + public $formatters = [ + self::FORMAT_HTML => 'yii\web\HtmlResponseFormatter', + self::FORMAT_XML => 'yii\web\XmlResponseFormatter', + self::FORMAT_JSON => 'yii\web\JsonResponseFormatter', + self::FORMAT_JSONP => [ + 'class' => 'yii\web\HtmlResponseFormatter', + 'useJsonp' => true, + ], + ]; /** * @var mixed the original response data. When this is not null, it will be converted into [[content]] * according to [[format]] when the response is being sent out. @@ -835,41 +861,17 @@ class Response extends \yii\base\Response if (isset($this->formatters[$this->format])) { $formatter = $this->formatters[$this->format]; if (!is_object($formatter)) { - $formatter = Yii::createObject($formatter); + $this->formatters[$this->format] = $formatter = Yii::createObject($formatter); } if ($formatter instanceof ResponseFormatterInterface) { $formatter->format($this); } else { throw new InvalidConfigException("The '{$this->format}' response formatter is invalid. It must implement the ResponseFormatterInterface."); } + } elseif ($this->format === self::FORMAT_RAW) { + $this->content = $this->data; } else { - switch ($this->format) { - case self::FORMAT_HTML: - $this->getHeaders()->setDefault('Content-Type', 'text/html; charset=' . $this->charset); - $this->content = $this->data; - break; - case self::FORMAT_RAW: - $this->content = $this->data; - break; - case self::FORMAT_JSON: - $this->getHeaders()->set('Content-Type', 'application/json; charset=UTF-8'); - $this->content = Json::encode($this->data); - break; - case self::FORMAT_JSONP: - $this->getHeaders()->set('Content-Type', 'text/javascript; charset=' . $this->charset); - if (is_array($this->data) && isset($this->data['data'], $this->data['callback'])) { - $this->content = sprintf('%s(%s);', $this->data['callback'], Json::encode($this->data['data'])); - } else { - $this->content = ''; - Yii::warning("The 'jsonp' response requires that the data be an array consisting of both 'data' and 'callback' elements.", __METHOD__); - } - break; - case self::FORMAT_XML: - Yii::createObject(XmlResponseFormatter::className())->format($this); - break; - default: - throw new InvalidConfigException("Unsupported response format: {$this->format}"); - } + throw new InvalidConfigException("Unsupported response format: {$this->format}"); } if (is_array($this->content)) { diff --git a/framework/web/XmlResponseFormatter.php b/framework/web/XmlResponseFormatter.php index 594034b..b9766bc 100644 --- a/framework/web/XmlResponseFormatter.php +++ b/framework/web/XmlResponseFormatter.php @@ -51,8 +51,12 @@ class XmlResponseFormatter extends Component implements ResponseFormatterInterfa */ public function format($response) { + $charset = $this->encoding === null ? $response->charset : $this->encoding; + if (stripos($this->contentType, 'charset') === false) { + $this->contentType .= '; charset=' . $charset; + } $response->getHeaders()->set('Content-Type', $this->contentType); - $dom = new DOMDocument($this->version, $this->encoding === null ? $response->charset : $this->encoding); + $dom = new DOMDocument($this->version, $charset); $root = new DOMElement($this->rootTag); $dom->appendChild($root); $this->buildXml($root, $response->data);