的 JsonView
和 XmlView
与 CakePHP 的 内容类型协商 功能集成在一起,使您可以创建 JSON 和 XML 响应。
这些视图类通常与 CakeControllerController::viewClasses()
一起使用。
您可以通过两种方式生成数据视图。第一种是使用 serialize
选项,第二种是创建普通的模板文件。
在您的 AppController
或某个单独的控制器中,您可以实现 viewClasses()
方法,并提供您想要支持的所有视图。
use Cake\View\JsonView;
use Cake\View\XmlView;
public function viewClasses(): array
{
return [JsonView::class, XmlView::class];
}
您可以选择使用 路由文件扩展名 启用 json 和/或 xml 扩展名。这将允许您使用自定义 URL(以响应类型的名称作为文件扩展名结尾)访问 JSON
、XML
或任何其他特殊格式的视图,例如 http://example.com/articles.json
。
默认情况下,如果未启用 路由文件扩展名,则会使用请求的 Accept
标头来选择应向用户呈现的格式类型。用于呈现 JSON
响应的示例 Accept
格式是 application/json
。
的 serialize
选项指示使用数据视图时应序列化哪些视图变量。如果您不需要在数据转换为 json/xml 之前进行任何自定义格式化,那么这可以让您跳过为控制器操作定义模板文件。
如果您需要在生成响应之前对视图变量进行任何格式化或操作,则应使用模板文件。的 serialize
值可以是字符串,也可以是要序列化的视图变量数组。
namespace App\Controller;
use Cake\View\JsonView;
class ArticlesController extends AppController
{
public function viewClasses(): array
{
return [JsonView::class];
}
public function index()
{
// Set the view vars
$this->set('articles', $this->paginate());
// Specify which view vars JsonView should serialize.
$this->viewBuilder()->setOption('serialize', 'articles');
}
}
您也可以将 serialize
定义为要组合的视图变量数组。
namespace App\Controller;
use Cake\View\JsonView;
class ArticlesController extends AppController
{
public function viewClasses(): array
{
return [JsonView::class];
}
public function index()
{
// Some code that created $articles and $comments
// Set the view vars
$this->set(compact('articles', 'comments'));
// Specify which view vars JsonView should serialize.
$this->viewBuilder()->setOption('serialize', ['articles', 'comments']);
}
}
将 serialize
定义为数组具有额外的好处,即在使用 XmlView
时自动追加一个顶级的 <response>
元素。如果您使用 serialize
的字符串值和 XmlView,请确保您的视图变量具有单个顶级元素。如果没有单个顶级元素,Xml 将无法生成。
如果您需要在创建最终输出之前操作视图内容,则应使用模板文件。例如,如果我们有一篇文章,其中一个字段包含生成的 HTML,那么我们可能希望将其从 JSON 响应中省略。在这种情况下,视图文件将很有用。
// Controller code
class ArticlesController extends AppController
{
public function index()
{
$articles = $this->paginate('Articles');
$this->set(compact('articles'));
}
}
// View code - templates/Articles/json/index.php
foreach ($articles as $article) {
unset($article->generated_html);
}
echo json_encode(compact('articles'));
您还可以进行更复杂的操作,或使用辅助函数进行格式化。数据视图类不支持布局。它们假定视图文件将输出序列化内容。
默认情况下,使用 serialize
时,XmlView 将使用 <response>
节点包装您的序列化视图变量。您可以使用 rootNode
选项为该节点设置自定义名称。
XmlView 类支持 xmlOptions
选项,该选项允许您自定义用于生成 XML 的选项,例如 tags
或 attributes
。
使用 XmlView
的一个示例是生成一个 sitemap.xml。这种文档类型要求您更改 rootNode
并设置属性。属性使用 @
前缀定义。
use Cake\View\XmlView;
public function viewClasses(): array
{
return [XmlView::class];
}
public function sitemap()
{
$pages = $this->Pages->find()->all();
$urls = [];
foreach ($pages as $page) {
$urls[] = [
'loc' => Router::url(['controller' => 'Pages', 'action' => 'view', $page->slug, '_full' => true]),
'lastmod' => $page->modified->format('Y-m-d'),
'changefreq' => 'daily',
'priority' => '0.5',
];
}
// Define a custom root node in the generated document.
$this->viewBuilder()
->setOption('rootNode', 'urlset')
->setOption('serialize', ['@xmlns', 'url']);
$this->set([
// Define an attribute on the root node.
'@xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',
'url' => $urls,
]);
}
JsonView 类支持 jsonOptions
选项,该选项允许您自定义用于生成 JSON 的位掩码。有关此选项的有效值,请参见 json_encode 文档。
例如,要以一致的 JSON 格式序列化 CakePHP 实体的验证错误输出,请执行以下操作:
// In your controller's action when saving failed
$this->set('errors', $articles->errors());
$this->viewBuilder()
->setOption('serialize', ['errors'])
->setOption('jsonOptions', JSON_FORCE_OBJECT);
使用 JsonView
时,您可以使用特殊的视图变量 jsonp
来启用返回 JSONP 响应。将其设置为 true
会使视图类检查是否设置了名为“callback”的查询字符串参数,如果设置了,则将 JSON 响应包装在提供的函数名称中。如果您想使用自定义查询字符串参数名称而不是“callback”,请将 jsonp
设置为所需的名称而不是 true
。
虽然您可以大部分时间使用 viewClasses
钩子方法,但如果您想要完全控制视图类选择,则可以直接选择视图类。
// src/Controller/VideosController.php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Http\Exception\NotFoundException;
class VideosController extends AppController
{
public function export($format = '')
{
$format = strtolower($format);
// Format to view mapping
$formats = [
'xml' => 'Xml',
'json' => 'Json',
];
// Error on unknown type
if (!isset($formats[$format])) {
throw new NotFoundException(__('Unknown format.'));
}
// Set Out Format View
$this->viewBuilder()->setClassName($formats[$format]);
// Get data
$videos = $this->Videos->find('latest')->all();
// Set Data View
$this->set(compact('videos'));
$this->viewBuilder()->setOption('serialize', ['videos']);
// Set Force Download
return $this->response->withDownload('report-' . date('YmdHis') . '.' . $format);
}
}