ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Yii 2 RESTful 响应格式详解:内容协商、数据序列化与 JSON 输出控制

Yii 2 RESTful 响应格式详解:内容协商、数据序列化与 JSON 输出控制 后端Web框架【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址https://gitcode.com/gh_mirrors/yi/yii2点击查看免费下载本篇技术指南聚焦 Yii 2 框架中 RESTful API 的响应格式处理机制围绕官方指南 rest-response-formatting.md 展开完整覆盖内容协商Content Negotiation→ 数据序列化Serializer→ 响应格式化Response Formatter的三阶段管线并结合仓库源码framework/rest、framework/web深入解析ContentNegotiator、Serializer、JsonResponseFormatter等核心类的底层实现。读完本文你将能够精确控制 RESTful API 的输出格式、分页信封结构、JSON 编码选项并理解 DAO 与 ActiveRecord 在数据类型转换上的差异。RESTful API 响应格式的三阶段管线当 Yii 2 处理一个 RESTful API 请求时响应格式的产生遵循如下步骤见 Controller.php 中的请求处理周期注释确定影响响应格式的因素媒介类型MIME type、语言、版本等这一过程称为内容协商content negotiation由yii\filters\ContentNegotiator过滤器完成。将资源对象转换为数组资源对象实现yii\base\ArrayableInterface与资源集合实现yii\data\DataProviderInterface通过yii\rest\Serializer序列化为 PHP 数组细节可参考 Resources资源。将数组转换为字符串通过注册在yii\web\Response应用组件formatters属性中的yii\web\ResponseFormatterInterface响应格式化器把数组序列化为 JSON、XML 等字符串并写入响应主体。这条管线在yii\rest\Controller::afterAction()中闭环动作执行返回资源对象或集合后serializeData() 通过Yii::createObject($this-serializer)-serialize($data)触发序列化随后Response组件在prepare()阶段调用已选定的 formatter 完成最终输出见 Response.php。内容协商ContentNegotiator 过滤器Yii 通过yii\filters\ContentNegotiator过滤器提供内容协商支持。RESTful API 基于控制器基类yii\rest\Controller在contentNegotiator行为中内置了这个过滤器见 Controller.php。该过滤器同时支持响应格式协商与应用语言协商两个维度见 ContentNegotiator.php配置formats属性时根据 GET 参数_format与AcceptHTTP 头协商响应格式命中后设置Response::format并同步更新Response::acceptMimeType与acceptParams配置languages属性时根据 GET 参数_lang与Accept-LanguageHTTP 头协商应用语言命中后设置Yii::$app-language。ContentNegotiator继承自ActionFilter并实现BootstrapInterface因此它既能作为控制器/模块的动作过滤器通过behaviors()声明作用于局部控制器或指定动作也能作为应用级 bootstrap 组件作用于整个应用对应实现为beforeAction()与bootstrap()两个入口ContentNegotiator.php。Accept 头驱动的格式选择当 RESTful API 请求携带如下 header 时Accept: application/json; q1.0, */*; q0.1协商结果为 JSON 格式响应curl -i观察到的完整响应如下$ curl -i -H Accept: application/json; q1.0, */*; q0.1 http://localhost/users HTTP/1.1 200 OK Date: Sun, 02 Mar 2014 05:31:43 GMT Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y X-Powered-By: PHP/5.4.20 X-Pagination-Total-Count: 1000 X-Pagination-Page-Count: 50 X-Pagination-Current-Page: 1 X-Pagination-Per-Page: 20 Link: http://localhost/users?page1; relself, http://localhost/users?page2; relnext, http://localhost/users?page50; rellast Transfer-Encoding: chunked Content-Type: application/json; charsetUTF-8 [ { id: 1, ... }, { id: 2, ... }, ... ]响应中的X-Pagination-*头与Link头由序列化器在分页场景下自动写入见下文数据序列化。协商过程的源码级拆解幕后流程如下在执行 RESTful API 控制器动作之前ContentNegotiator检查请求的AcceptHTTP 头并将yii\web\Response::format配置为json动作执行并返回资源对象或集合后Serializer将结果转换为数组最后由JsonResponseFormatter将数组序列化为 JSON 字符串并放入响应主体。negotiateContentType()ContentNegotiator.php的具体协商逻辑为若配置了formatParam默认_format且请求携带该 GET 参数直接校验该格式是否存在于formats中——存在则立即采用若参数为数组则抛出BadRequestHttpException400格式不受支持则抛出NotAcceptableHttpException406。否则遍历Request::getAcceptableContentTypes()返回的可接受类型按 q 值排序首个命中formats键的 MIME 类型即被选中。若没有匹配但请求包含*/*通配则使用formats中声明的第一个格式兜底。若请求的媒介类型均不被支持抛出NotAcceptableHttpException406。此外当formats中声明了多种格式或languages中声明了多种语言时协商会自动向响应添加Vary: Accept或Vary: Accept-Language头见 negotiate()这对 HTTP 缓存层的正确工作至关重要。扩展新的响应格式默认情况下RESTful API 同时支持 JSON 和 XML 两种格式对应yii\rest\Controller::behaviors()中的application/json Response::FORMAT_JSON与application/xml Response::FORMAT_XML。要支持新的格式需在contentNegotiator过滤器中配置yii\filters\ContentNegotiator::formats属性例如在 API 控制器类中新增 HTML 支持use yii\web\Response; public function behaviors() { $behaviors parent::behaviors(); $behaviors[contentNegotiator][formats][text/html] Response::FORMAT_HTML; return $behaviors; }formats属性的键为 MIME 类型值必须是yii\web\Response::formatters中支持的响应格式名称。Response组件默认注册的格式化器见 defaultFormatters()格式常量值默认格式化器Response::FORMAT_HTMLhtmlyii\web\HtmlResponseFormatterResponse::FORMAT_XMLxmlyii\web\XmlResponseFormatterResponse::FORMAT_JSONjsonyii\web\JsonResponseFormatterResponse::FORMAT_JSONPjsonpyii\web\JsonResponseFormatter启用useJsonpFORMAT_RAW不经过格式化器直接以原始数据作为响应内容。在 Response::prepare() 中若formatters[$format]不是已实例化对象会通过Yii::createObject()惰性创建并要求其实例必须实现ResponseFormatterInterface否则抛出InvalidConfigException。数据序列化Serializeryii\rest\Serializer负责将资源对象或集合转换为数组它把实现yii\base\ArrayableInterface的对象前者主要由资源对象实现即yii\base\Model及其子类如yii\db\ActiveRecord和实现yii\data\DataProviderInterface的对象资源集合统一序列化。序列化的类型分派Serializer::serialize()Serializer.php按以下优先级分派处理带验证错误的Model→serializeModelErrors()将响应状态码设为 422Data Validation Failed.并输出形如[[field ..., message ...], ...]的错误数组Serializer.php实现Arrayable的对象→serializeModel()结合fields/expand请求参数调用$model-toArray($fields, $expand)实现JsonSerializable的对象→ 直接调用jsonSerialize()实现DataProviderInterface的对象→serializeDataProvider()处理集合与分页普通数组→ 递归地对每个元素执行上述分派其他类型原样返回。单资源序列化与字段筛选serializeModel()Serializer.php通过getRequestedFields()从请求参数读取字段选择信息fieldsParam默认fields逗号分隔的字段列表对应Model::fields()中声明的默认字段expandParam默认expand逗号分隔的额外字段列表对应Model::extraFields()中声明的扩展字段。例如GET /users?fieldsid,email只返回id与emailGET /users?expandprofile则追加profile字段嵌套展开如expandpost.author也受支持详见 rest-resources.md。集合序列化与分页头serializeDataProvider()Serializer.php是集合处理的核心若preserveKeys为false默认通过array_values()重新索引模型数组为true时保留原始数组键可输出带键索引的 JSON 对象逐个序列化模型serializeModels()对Arrayable对象调用serializeModel()对普通数组调用ArrayHelper::toArray()若数据提供器启用了分页调用addPaginationHeaders()写入分页相关 HTTP 头HEAD 请求直接返回null响应主体为空未配置collectionEnvelope时直接返回模型数组。addPaginationHeaders()Serializer.php负责向响应写入分页信息对应上文响应示例中的各头头名称含义X-Pagination-Total-Count数据总条数X-Pagination-Page-Count总页数X-Pagination-Current-Page当前页码从 1 开始X-Pagination-Per-Page每页条数Linkself、next、last等分页链接url; relrel格式使用 collectionEnvelope 定制集合信封可以通过设置yii\rest\Controller::serializer属性类型为字符串或配置数组来定制序列化器。例如希望直接在响应主体中包含分页信息以简化客户端开发可配置yii\rest\Serializer::collectionEnvelope属性use yii\rest\ActiveController; class UserController extends ActiveController { public $modelClass app\models\User; public $serializer [ class yii\rest\Serializer, collectionEnvelope items, ]; }此时请求http://localhost/users得到的响应变为HTTP/1.1 200 OK Date: Sun, 02 Mar 2014 05:31:43 GMT Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y X-Powered-By: PHP/5.4.20 X-Pagination-Total-Count: 1000 X-Pagination-Page-Count: 50 X-Pagination-Current-Page: 1 X-Pagination-Per-Page: 20 Link: http://localhost/users?page1; relself, http://localhost/users?page2; relnext, http://localhost/users?page50; rellast Transfer-Encoding: chunked Content-Type: application/json; charsetUTF-8 { items: [ { id: 1, ... }, { id: 2, ... }, ... ], _links: { self: { href: http://localhost/users?page1 }, next: { href: http://localhost/users?page2 }, last: { href: http://localhost/users?page50 } }, _meta: { totalCount: 1000, pageCount: 50, currentPage: 1, perPage: 20 } }该响应结构的生成逻辑在 serializeDataProvider() 与 serializePagination() 中_links通过Link::serialize($pagination-getLinks(true))生成_meta由Pagination::toArray()语义的四个键构成。信封名称均可定制linksEnvelope默认_links与metaEnvelope默认_meta属性自 2.0.4 起可用。不设置collectionEnvelope时分页信息仅通过 HTTP 头暴露集合直接输出为数组。控制 JSON 输出JsonResponseFormatterJSON 响应由yii\web\JsonResponseFormatter生成内部使用yii\helpers\Json即BaseJson进行编码。该格式化器提供以下可配置项见 JsonResponseFormatter.phpprettyPrint默认false是否输出人类可读的美化 JSON。启用时向encodeOptions追加JSON_PRETTY_PRINT在开发调试阶段非常实用encodeOptions默认JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE即数值 320传递给Json::encode()的json_encode()选项用于控制 JSON 编码行为如转义规则、数值格式化等useJsonp默认false是否使用 JSONP 输出格式。启用时要求响应数据为包含data与callback两个键的数组输出形如callback(data);Content-Type变为application/javascript; charsetUTF-8contentType默认null自定义Content-Type头为null时依据useJsonp自动选择application/json或application/javascriptkeepObjectType默认跟随Json::$keepObjectType避免零索引键对象被编码为数组与原生json_encode()行为对齐2.0.44 起可用。format()方法JsonResponseFormatter.php先设置Content-Type头再根据useJsonp分发到formatJsonp()或formatJson()。其中formatJson()在prettyPrint为真时通过位或运算追加JSON_PRETTY_PRINT并调用Json::encode($response-data, $options)生成内容JsonResponseFormatter.php若数据为null且无内容则输出字符串null。在 response 组件中配置格式化器格式化器在应用配置的response组件formatters属性中配置配置机制详见 概念-配置如下所示response [ // ... formatters [ \yii\web\Response::FORMAT_JSON [ class yii\web\JsonResponseFormatter, prettyPrint YII_DEBUG, // use pretty output in debug mode encodeOptions JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE, // ... ], ], ],配置数组的键是格式名如json值是创建格式化器对象的配置。Response::init()中通过array_merge($this-defaultFormatters(), $this-formatters)将自定义配置与默认格式化器合并Response.php因此这里的键json会覆盖默认的JsonResponseFormatter配置。encodeOptions支持 PHP 原生json_encode()的全部选项常量例如JSON_UNESCAPED_SLASHES不转义斜杠、JSON_UNESCAPED_UNICODE不转义 Unicode 字符、JSON_NUMERIC_CHECK数字字符串转数字、JSON_PRETTY_PRINT美化输出等可按位或组合使用。JSONP 输出当需要跨域场景不使用 CORS时可将格式配置为Response::FORMAT_JSONP并在控制器中返回[data $data, callback $callbackName]结构。formatJsonp()JsonResponseFormatter.php会输出callback(data);形式的 JavaScript 代码其中数据经Json::htmlEncode()处理以确保 HTML 安全若数据缺少data/callback键则记录一条 warning 并输出空内容。数据类型一致性DAO 与 ActiveRecord 的差异当使用 DAO数据库访问层 从数据库返回数据时所有数据都会表示成字符串数据库驱动原生返回的裸类型这不总是符合预期——尤其是数值列在 JSON 中本应表现为数字如id: 1而非id: 1时。而当使用 ActiveRecord 层从数据库检索数据时在yii\db\ActiveRecord::populateRecord()中填充数据的过程中数字列的值会被转换为整数如intval从而在 JSON 输出中呈现为 JSON 数字而非字符串。因此若你的 RESTful API 完全基于 ActiveRecord 构建JSON 中的数值类型通常是正确的若混用或直接使用 DAO 查询需要注意对返回的标量类型做显式转换或在encodeOptions中使用JSON_NUMERIC_CHECK让json_encode()自动将数字字符串编码为 JSON 数字需结合数据语义谨慎使用。测试验证仓库测试目录提供了与本文主题直接对应的测试用例可作为理解行为边界的参考tests/framework/rest/SerializerTest.php覆盖Serializer的模型错误序列化422 状态码与错误结构、serialize()类型分派、分页头写入、collectionEnvelope信封输出等场景tests/framework/filters/ContentNegotiatorTest.php覆盖ContentNegotiator的Accept头解析、_formatGET 参数、格式不可接受时抛出NotAcceptableHttpException等行为。小结Yii 2 将 RESTful API 响应格式处理拆解为职责清晰的三层ContentNegotiator负责基于Accept头与_format参数完成格式/语言协商并设置Response::formatSerializer负责将资源对象与数据提供器转换为数组并负责分页头与信封结构Response::formatters中注册的ResponseFormatterInterface实现默认含 HTML/XML/JSON/JSONP 四种负责最终字符串化。理解这三层各自的职责边界与可配置属性即可对 API 输出的媒介类型、字段筛选、分页结构、JSON 编码细节实现全面掌控。赞分享后端Web框架【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址https://gitcode.com/gh_mirrors/yi/yii2点击查看免费下载相关推荐Yii 2 RESTful API 响应格式配置实战内容协商、数据序列化与 JSON 输出控制Yii 2 RESTful API 响应格式配置实战内容协商、数据序列化与 JSON 输出控制 RESTful API 的响应格式决定了客户端拿到的是 JSO后端Web框架Yii 2 RESTful API 响应格式化完全指南内容协商、数据序列化与 JSON 输出控制Yii 2 RESTful API 响应格式化完全指南内容协商、数据序列化与 JSON 输出控制 在 Yii 2 框架中RESTful API 的响应格式化后端Web框架Yii2 RESTful API 响应格式化指南内容协商、Serializer 序列化与 JSON/XML 输出控制Yii2 RESTful API 响应格式化指南内容协商、Serializer 序列化与 JSON/XML 输出控制 导读 在 Yii2 中一次 RESTf后端Web框架上一篇腾讯混元小模型全系列部署详解从0.5B到7B的本地化落地指南下一篇Xray编辑器配色方案从CSS变量到主题切换动画实现创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表