速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

企业级 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:533025

评论

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

2023英特尔on技术创新大会:英特尔研究院展示多项技术“魔法”

E科讯

聊聊复杂网络环境下hdfs的BlockMissingException异常|参数dfs.client.use.datanode.hostname

明哥的IT随笔

hdfs TCP/IP

代理价格监控和分布式价格监控有什么不同点?

巨量HTTP

http代理 分布式代理

你眼中的程序员 vs 程序员眼中的自己,是时候打破刻板印象了丨KubeCon 主题活动

阿里巴巴云原生

阿里云 容器 云原生 AIGC

《异构融合计算技术白皮书》正式发布,龙蜥助力新一代信息技术发展

OpenAnolis小助手

开源 白皮书 龙蜥社区 世界计算大会 异构融合计算

深度学习之“线下标注”

矩视智能

深度学习 机器视觉

英特尔研究院副总裁、英特尔中国研究院院长宋继强:英特尔技术为全行业带来卓越贡献

E科讯

RTC才是物联网企业的杀手锏!

X2Rtc

音视频 物联网 IoT RTC

【聚梦想 创非凡】2023荣耀开发者沙龙 ● 广州站 精彩回顾

荣耀开发者服务平台

互联网 用户增长 云服务 生态合作 行业经验

一站式运营营销平台

Quincy

运营 用户增长技术 营销数字化 用户留存 `后端

OWASP Top 10漏洞解析(1)- A1:Broken Access Control 访问控制失效

华为云PaaS服务小智

云计算 华为云 漏洞分析

C4D在影视动画制作中的局限性

Finovy Cloud

C4D

Vue3中的Ref与Reactive:深入理解响应式编程

互联网工科生

前端框架 Vue 3

大厂旗下的APP们成为自研大模型的“磨刀石”|TE洞察

TE智库

人工智能 BAT 大模型

【开源】低代码引擎 TinyEngine 源码正式开放啦!!!

OpenTiny社区

开源 前端 低代码

Java虚拟线程简介

FunTester

探索全球 AI 产品市场:50 个国内外导航网站助力推广

出海的猹

AI 导航网站 出海

2024成都电子展|成都国际电子信息展览会

AIOTE智博会

电子信息展 成都电子展 电子信息博览会

未来PC将是云电脑? 算力优势成为关键

青椒云云电脑

云电脑

中国首个接入大模型的Linux操作系统;ChatGPT支持图片和语音输入;抖音上线方言自动翻译功能丨RTE开发者日报 Vol.57

声网

开放原子开源大赛 | Pika赛题正式启动,诚邀报名!

开放原子开源基金会

流量治理的基石——基于字节码增强的全链路流量标签透传

华为云开源

微服务 微服务架构 字节码

【架构篇】Supabase的Postgrest组件架构介绍

张文平

数据库 postgresql 缓存 多表联查 Postgrest

1024程序员节之天马低代码开发者大赛篇

Openlab_cosmoplat

低代码 1024程序员节

[Go 夜读 第 148 期] Excelize 构建 WebAssembly 版本跨语言支持实践

xuri

开源 Excel webassembly Go 语言 Excelize

开始报名!龙蜥社区走进 Arm MeetUp 议程硬核剧透来了

OpenAnolis小助手

开源 操作系统 arm Meetup 龙蜥社区

真香!为什么设计行业流行部署云桌面?

青椒云云电脑

云桌面

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