AICon上海|与字节、阿里、腾讯等企业共同探索Agent 时代的落地应用 了解详情
写点什么

企业级 REST = 自定义、创造和标准化 Media Type

  • 2011-06-09
  • 本文字数:3918 字

    阅读完需:约 13 分钟

关于 REST,经常容易引起困惑的一个问题是:在 Machine-to-machine 的 REST 应用中, 客户端怎么才能在没有任何预先知识的情况下,就能跟随服务器端返回的超文本,从而自适应地将应用程序逻辑进行下去?

答案是不可能。Client 端必须提前了解足够多的 server 端返回的超文本的知识,才能跟随其中的指示将逻辑进行下去。那跟 SOAP、WSDL 之类的又有啥区别? 答案在于 REST 要求的“足够知识”要远远弱于 WSDL 对服务接口定义的约束,其灵活性则高于 WSDL。毕竟,REST 架构风格着眼于生命周期较长的、不断演化的、跨组织边界的应用程序。可以用来满足 HATEOAS 这个 REST 约束的武器就是 Media Type: 扩展已有的 Media Type,发明新的 Media Type,并在合适的范围内标准化

来看两个例子。

Web 的成功与其说是 HTTP 的成功,不如说是 HTML 的成功,或者说 text/html 这种 Media Type 的成功。HTML 定义了一组有限的、标准化的、特定领域的标签。所有的 HTML 客户端都能理解,并按照自己的能力渲染出 Web 服务器返回的任何合法的 HTML。从全功能的 Chrome/FireFox/Safari/IE,到只是显示文本的 lynx,到资源受限环境如手机中的各种浏览器,都能将多姿多彩的 Web 呈现给最终用户,并引导他们进行下一步交互。

最经常被拿来作为 machine-to-machine REST 应用成功案例的则是 RSS/ATOM。这族标准定义了新的 Media Type:application/rss+xml、application/atom+xml。不出意外地,这组 Media Types 定义了一组有限的、标准化的、特定领域的标签。每一个 RSS/ATOM 客户端应用都可以从某个资源的 application/rss(atom)+xml 的表述开始,顺藤摸瓜地遍历每个感兴趣的资源。这些客户端都理解每一个 RSS/ATOM 定义的标签。服务端可以自由地改变新资源的 URI/URL Template 而不必担心破坏现有的 Client,因为这些 Client 并不依赖于预先设定的 URL Template,而仅仅依赖于一个 Root 的表述,及标准的 link relations。

回到我们的问题。那么 REST 要求的“足够知识”要弱到什么程度,才能既使客户端能理解服务器给出的线索,又不致于耦合得太紧以致服务器和客户端无法独立演化? 观察上面两个例子,它们有以下共同特点:

  1. Response 是 Well-formed,可以被客户端解析。这是废话,除了 AI 应用,绝大部分网络应用都得有明确定义的协议。但这意味着 REST 应用中也必须明确定义客户端和服务器数据交换的格式
  2. Response 中包含了当前上下文 (或资源) 相关的信息,但对我们这个问题来说,更重要的是包含了对其它资源进行访问的线索。是的,它是用最最基本最最普通的 link 来实现的,足够弱。那语义是否足够清晰到客户端能理解每一个 link,能够选择正确的 link 并知道如何访问呢? 我们来看看 link 上都能承载啥语义 (from http://www.ietf.org/rfc/rfc5988.txt ):
复制代码
Link           = "Link" ":" #link-value
link-value     = "<" URI-Reference ">" *( ";" link-param )
link-param     = ( ( "rel" "=" relation-types )
                 | ( "anchor" "=" <"> URI-Reference <"> )
                 | ( "rev" "=" relation-types )
                 | ( "hreflang" "=" Language-Tag )
                 | ( "media" "=" ( MediaDesc | ( <"> MediaDesc <"> ) ) )
                 | ( "title" "=" quoted-string )
                 | ( "title*" "=" ext-value )
                 | ( "type" "=" ( media-type | quoted-mt ) )
                 | ( link-extension ) )
  link-extension = ( parmname [ "=" ( ptoken | quoted-string ) ] )
                 | ( ext-name-star "=" ext-value )
  ext-name-star  = parmname "*" ; reserved for RFC2231-profiled
                                ; extensions.  Whitespace NOT
                                ; allowed in between.
  ptoken         = 1*ptokenchar
  ptokenchar     = "!" | "#" | "$" | "%" | "&" | "'" | "("
                 | ")" | "*" | "+" | "-" | "." | "/" | DIGIT
                 | ":" | "<" | "=" | ">" | "?" | "@" | ALPHA
                 | "[" | "]" | "^" | "_" | "`" | "{" | "|"
                 | "}" | "~"
  media-type     = type-name "/" subtype-name
  quoted-mt      = <"> media-type <">
  relation-types = relation-type
                 | <"> relation-type *( 1*SP relation-type ) <">
  relation-type  = reg-rel-type | ext-rel-type
  reg-rel-type   = LOALPHA *( LOALPHA | DIGIT | "." | "-" )
  ext-rel-type   = URI

这里我们重点考察一下 relmedia-type 属性。

在 ATOM( http://www.ietf.org/rfc/rfc4287.txt ) 里,缺省定义了 5 种 link relations,分别是 alternate,related,self,enclosure 和 via。标准赋予它们明确的语义,比如 alternate 表示 link 所指向的目标是当前资源的备用版本。客户端因此可以理解每个 link 的含义,自动或引导用户完成后面的操作。如何完成?则牵扯到 media-type。比如如果 media-type 定义的是 audio/mp4,客户端则可以显示播放选项或自动播放或干脆忽略。

这里 link 以及 media-type 等属性帮助形成了一个递归的过程。我们从某个 link 开始,知晓它的 media-type(所谓知晓它的 media-type,就是能理解这种 media-type 定义的每一个元素的语义),我们就能够得到这个 link 所指向的资源以这种 media-type 表现出来的一种表述,其中包含了其它可用的 link 以及 media-type,可以让这个过程一直继续下去,直到客户端觉得可以了或者服务端返回了不包含任何其它 link 的表述。

然而初始的 media-type 和 link relations 总是有限的,我们如何应对现有 media-type 表达不了的语义?这就是 HTML 和 RSS/ATOM 例子包含的第三个要素。

  1. 定义领域相关的 media-type。HTML 的问题域是如何表达各种显示效果,RSS/ATOM 则是如何发布信息。media-type 以及 link relations 等都是可扩展的。我们要做的就是为我们的特定领域的应用定义扩展,如果现存的标准化的元素不够用的话。这里有一个问题,就是 REST 应用范围的问题。如果我们的应用是面向 Internet 的,面向无数已知的未知的客户端应用的,则意味着我们必须尽可能标准化我们扩展的 media-type,或者至少让它广泛接受。而对于企业内部以 REST 架构的应用,我们同样面临标准化的问题,只不过范围可能小一点,至少是企业内部需要有共同接受的 media-type。
    前面我们看到了良好定义的 media-type 是如何帮助客户应用理解 server 端的 response,而又不致紧密耦合服务端的实现的。我们需要一个企业开发的例子来验证一下我们的理解。比如要开发一个企业内部不同应用之间共享用户信息的应用,包括权限信息,当前可以执行的操作,可以访问的应用,以及应用之间共享的 Preference 设置等。我们可以定义一种叫 application/vnd.tw.account+xml 的 media-type 可以有以下的片段
复制代码
<account>
<preference>
<link rel="preference" media-type="application/vnd.tw.account.preference+xml" href="http://xxx/preference" />
<link rel="edit" media-type="text/html" href="http://xxx/preference/editForm">
</preference>
<subscribed-services>
<subscribed-service>
<name>Taxes</name>
<link rel="subscription" media-type="text/html" href="http://taxes.xxx.com" />
</subscribed-service>
<subscribed-service>
<name>Audit</name>
<link rel="subscription" media-type="text/html" href="http://audit.xxx.com" />
</subscribed-service>
</subscribed-services>
<contacts>
<contact>
<name>Tom</name>
<link rel="contact" media-type="application/xfn+xml" href="http://account.xxx.com/tom" />
</contact>
</contacts>
</account>

这样无论是交互式客户端像浏览器还是自动化的后台应用,都可以按照这段 media 的指示进行自己感兴趣的操作。

URL Template Considered Harmful

如果这些都实现的话,就可以有一个推论:URL Template 不是必须的,甚至是有害的。它限制了服务器端的变化。客户应用应总是从 Root Resource 开始,在特定 Media Type 的引导下解析出其它的 URI,给予服务程序演化的灵活性而不是按照 URL Template 这种预先的知识来推算。

用 REST 做过很多项目的 Xu Hao 对 Url template 也有同样的看法:“URL template 在我看来是有害的,它是一种隐含的服务器和客户端约定。此外还有一点,由于大多数 URL template 是用来表达 state transfer 的 URL 的,比如 /xxx/approve 之类的,使得客户端必须了解服务器的状态转移的细节,这在我看来是一个更大的问题。这使得客户端和服务器的耦合变得更加紧密,同时这种风格极度鼓励服务器借由客户端来维持状态的一致性”。

很多 REST 相关的文章都把大量的篇幅给了 HTTP,比如 HTTP Verbs POST/GET/PUT/DELETE,HTTP Status Code 等,而 media type 着墨不多。Xu Hao 在社区中曾很多次的提起自定义 Media Type,对 REST 应用不多可能当时不太明白,现在越来越多的人认识到扩展和标准化更多的 media type 才能更多的发挥 REST 的潜力。

参考资料

2011-06-09 21:533171

评论

发布
暂无评论
发现更多内容

浏览器拨测:将网站护航的阵地再前推一米

阿里巴巴云原生

阿里云 云原生 云拨测

赣州银行新核心成功投产上线,分布式存储筑牢数字化转型根基

XSKY星辰天合

数据同步、流计算全面强化,TDengine 3.3.4.3 版本正式发布

TDengine

数据库 tdengine 时序数据库

盘点本轮牛市币价再创新高的「老山寨」

TechubNews

AI智能体的开发流程

北京木奇移动技术有限公司

AI应用 AI智能体

探索1688阿里巴巴API接口:图片搜索商品列表(拍立淘)与店铺所有商品列表的实战应用

代码忍者

API 接口 pinduoduo API

云桌面年终狂欢,多重福利等你来拿!

Finovy Cloud

云主机 云服务器 云桌面 云电脑

从仪表盘探索 MongoDB 关键指标

巴辉特

人事人才信息管理系统:2024年10大推荐系统

易成研发中心

MES系统在传统制造业中的作用与战略价值

积木链小链

mes

汽车摆臂行业生产设备数据采集及集成

万界星空科技

mes #汽车 设备数据采集 汽车摆臂行业 汽车摆臂mes

云数仓ByteHouse的湖仓一体设计与实践

字节跳动数据平台

大数据 数据仓库 湖仓一体

一行代码都不改,Golang 应用链路指标日志全知道!

阿里巴巴云原生

阿里云 云原生

判断TikTok节点IP的纯净度高不高的几个方法

Ogcloud

tiktok运营 tiktok节点 tiktok网络 tiktok封号 tiktok代理节点

定制化NFT链游DAPP开发:一站式解决方案助力游戏创新

区块链软件开发推广运营

交易所开发 dapp开发 链游开发 公链开发 代币开发

全渠道商品计划软件:时尚鞋服企业的数字化新引擎与AI赋能

第七在线

工业公辅车间数智化节能头部企业,蘑菇物联选择 TDengine 升级 AI 云智控

TDengine

数据库 tdengine 时序数据库

YashanDB在地理信息系统(GIS)领域的关键功能和技术优势

YashanDB

yashandb 崖山数据库 地理信息系统

苹果m1能玩steam吗,苹果笔记本玩steam游戏教程

阿拉灯神丁

游戏 MacBook steam M1芯片 CrossOver Mac下载

知乎启用AutoMQ替换Kafka,开辟成本优化与运维提效新纪元

AutoMQ

Java 数据库 kafka 客户案例

探索1688阿里巴巴API接口:揭秘商品详情与关键字搜索的奥秘

代码忍者

API 接口 pinduoduo API

在“黑天鹅”时代,如何锻造创业者的认知韧性?| 陶建辉演讲分享

TDengine

数据库 tdengine 时序数据库

TikTok账号封号原因有哪些?

Ogcloud

TikTok tiktok运营 TikTok养号 tiktok封号

客户案例|大模型加持智能会议,金融机构实现“写纪要”到“审纪要”的飞跃

澜舟孟子开源社区

人工智能 大模型 智能办公

翻倍只是山寨币季的点火阶段:市场分歧与未来趋势

区块链软件开发推广运营

交易所开发 dapp开发 链游开发 公链开发 代币开发

企业级REST = 自定义、创造和标准化Media Type_Java_李光磊_InfoQ精选文章