REST媒体类型爆炸

48
在我试图使用REST架构风格重新设计一个现有应用程序时,我遇到了一个问题,我想称之为“Mediatype Explosion”。然而,我不确定这是否真的是REST的问题或固有好处。为了解释我的意思,看下面的例子:
我们应用程序的一小部分如下:
集合-》集合-》项
即顶层是集合的集合,每个集合又是项的集合。
此外,每个项具有8个属性,这些属性可以单独读取和写入。尝试将上述层次结构作为RESTful资源公开将使我拥有以下媒体类型:
application/vnd.mycompany.collection-of-collections+xml
application/vnd.mycompany.collection-of-items+xml
application/vnd.mycompany.item+xml

此外,由于每个项目都有8个属性可单独读写,因此会产生另外8种媒体类型。例如,“项”中“值”属性的一个媒体类型可能是:

application/vnd.mycompany.item_value+xml

正如我之前提到的,这只是我们应用程序的一小部分,我预计还需要公开几个不同的集合和项目。

我的问题是:

  1. 拥有如此众多的媒体类型是否存在问题?
  2. 有什么替代设计方法可以避免这种媒体类型爆炸?

我也意识到上面的设计非常细粒度,特别是暴露了每个项目的单独属性,并为其单独定义了媒体类型。然而,如果将其变得粗略,则意味着在事实上客户端只需要读取或写入项目的单个属性时,会传输不必要的数据。您将如何处理这样的设计问题?

6个回答

35

减少所需媒体类型的一种方法是使用定义为容纳其他媒体类型列表的媒体类型。这可用于您的所有集合。一般来说,列表倾向于具有一致的行为集。

你可以自己创建vnd.mycompany.resourcelist,也可以重用像Atom collection这样的东西。

关于特定资源表示(如vnd.mycompany.item),您可以做什么取决于客户端的特征。它在浏览器中吗?可以进行代码下载吗?您的客户端是丰富的UI,还是数据处理客户端?

如果客户端要进行特定的数据处理,则必须坚持使用精确的媒体类型,您可能会最终拥有大量的媒体类型。但看到积极的一面,您将拥有较少的媒体类型,而不是使用SOAP时所需的命名空间!

请记住,媒体类型是您的契约,如果应用程序需要与客户端定义许多契约,那么就是这样。但是,我不会走得太远,以至于定义交换单个属性值的合同。如果您感到需要这样做,那么您在设计方面出了问题。分布式接口设计需要进行粗略的对话,而不是冗余的对话。


Darrel, 感谢您宝贵的建议。 目前我确实看到在我们的应用程序中需要定义合同以交换单个属性值。但是您是正确的,也许我需要在这里采取不同的方法... - Suresh Kumar

31

我认为我终于从Ian Robinson的演示中得到了我寻求的上述问题的澄清,并想在这里分享。

最近,我在Jim Webber的博客文章中发现了声明“用于帮助调整超媒体引擎的媒体类型,用于结构的模式”。然后我找到了Thoughtworks的Ian Robinson所做的这个演示。这个演示是我见过的最好的演示之一,它提供了非常清晰的媒体类型和模式语言的角色和职责的理解(整个演示是一种享受,我强烈推荐给所有人)。特别留意标题为“您选择了应用程序/xml,你这个混蛋。”和“自定义媒体类型”的幻灯片。Ian清楚地解释了模式和媒体类型的不同角色。简而言之,这是我从Ian的演示中得出的结论:

一个媒体类型描述包括识别超媒体控件的处理模型,并定义适用于该类型资源的方法。识别超媒体控件意味着“如何识别链接?”在XHTML中,链接是基于标签进行识别的,而RDF对此有不同的语义。媒体类型帮助识别的下一件事情是哪些方法适用于给定媒体类型的资源?一个很好的例子是ATOM(application/atom+xml)规范,它提供了关于超媒体控件的非常丰富的描述;它们告诉我们如何定义链接元素?当我们解引用URI时可以期望能做什么,因此它实际上告诉我们可以应用于资源的方法。资源表示的结构信息不是媒体类型描述的一部分,也不包含在其中,而是作为实际表示的适当架构的一部分提供,即媒体类型规范不一定会对表示的结构产生任何影响。
那么这对我们意味着什么?简单地说,我们不需要为描述每个资源使用单独的媒体类型,就像我原来的问题中所描述的那样。我们只需要一个媒体类型来覆盖整个应用程序。这可以是全新的自定义媒体类型,也可以是重用现有标准媒体类型的自定义媒体类型,或者更好的是,只需一个标准媒体类型即可在我们的应用程序中重用而无需更改。
希望这可以帮到您。

1
这样的媒体类型的一个很好的例子是HAL。它支持jsonxml,在面向超媒体驱动的API时,专注于尽可能实用主义。 - edsioufi
@edsioufi - 是的,我们实际上已经开始在一个项目中同时使用XHTML和HAL+JSON,到目前为止似乎可以完成工作。 - Suresh Kumar
@edsioufi 说得好。我发现这个HAL入门指南对于理解application/hal+json的使用非常有用。 - mb21

9
在我看来,这是REST概念的薄弱环节。作为一种架构和接口风格,REST非常出色,Roy F.和其他人所做的工作已经大大推动了技术的发展。但标准媒体类型所能传输的信息是有上限的,不能涵盖所有信息。
要使人们理解和使用你的类REST API,他们需要理解数据的含义。有些API的媒体类型可以讲述大部分的故事;例如,如果你有一个文本转语音的API,输入的媒体类型是text/plain,输出的媒体类型是audio/mp4,那么熟悉相关主题的人可能就足够了。在这种情况下,输入为文本,输出为音频,这可能已经够用了。
但许多API不能仅通过媒体类型传达其含义。比如说你有一个管理航空机票的API。输入和输出大多是数据。每个API的媒体类型都可以是application/json或application/xml,因此媒体类型并不能传递太多信息。所以你需要查看输入输出中的每一个字段。也许有一个名为“price”的字段。是美元还是美分?USD或其他货币?没有一个标准的媒体类型来定义这些问题。我不知道用户要如何回答这些问题,除非(a)命名非常详细,例如“price_pennies_in_usd”,或者(b)提供文档。更不用说格式约定了。账号号码是带破折号还是不带?字母必须全大写等等。没有一种标准的媒体类型定义这些问题。
当客户端不需要对数据进行语义理解时,情况就很简单。这种情况下工作得非常好。浏览器可以视觉化地呈现任何兼容的文档,并与任何兼容的资源进行交互。这基本上是“媒体”用例。
但当客户端(或实际上是客户端背后的开发人员/用户)需要理解数据的含义时,情况就完全不同了。数据不是媒体。除了文档以外,没有其他方法可以解释所有数据在真实世界中的含义和细微差别。这是“数据”用例。
REST过于学术化的定义只适用于“媒体”用例。对于其他用例,它并不起作用,需要补充类似文档这样的非纯粹但有用的东西来帮助理解。

1
超媒体类型的存在是为了告诉客户端如何与数据交互,而不是数据本身的含义。例如,Collection+JSON和Siren两种通用的超媒体类型,都可以轻松地描述集合、集合项(资源)、对所需资源执行的操作以及相关链接(例如针对分页信息的第一个/最后一个/前一个/后一个链接)。链接是至关重要的,这类似于Web浏览器与HTML交互的粗略模拟。 - Eric Elliott

2
你正在使用媒体类型来传达应该存储在表示本身中的数据细节。因此,你可以只使用一个媒体类型,比如“application/xml”,然后你的XML表示将会是这样的:
<collection-of-collections>
    <collection-of-items>
        <item>
        </item>
        <item>
        </item>
    </collection-of-items>
    <collection-of-items>
        <item>
        </item>
        <item>
        </item>
    </collection-of-items>
</collection-of-collections>

如果您担心发送过多数据,可以将XML替换为JSON。另一种节省写入和读取字节数的方法是使用gzip编码,这将削减约60-70%的数据量。除非您需要超高性能,否则这些方法中的任何一个都应该适用于您。(为了获得更好的性能,您可以使用非常简洁的手工字符串,甚至可以降级到自定义二进制TCP / IP协议。)
编辑:您之一的担忧是:
引用: “使表示变粗糙意味着我最终会在传输无关紧要的数据时,实际上客户端只需要读取或写入某个项目的单个属性。”
在任何网络服务中,发送消息时存在相当多的开销(每个HTTP请求可能会花费数百字节的起始行和请求头,每个HTTP响应同样如此,如this example所示)。因此,通常情况下,您希望拥有更少的细粒度表示。因此,您将编写客户端以请求这些更大的表示,并将它们缓存到一些便捷的内存数据结构中,您的程序可以从中多次读取数据(但一定要遵守服务器设置的HTTP过期日期)。在向服务器写入数据时,通常会将一组更改组合到您的内存数据结构中,然后将更新作为单个HTTP PUT请求发送到服务器。
你应该获取 Richardson 和 Ruby 的《RESTful Web Services》一书,这是一本关于如何设计 REST Web 服务的非常优秀的书籍,比我能讲得更清楚。如果你在使用 Java,我强烈推荐使用 RESTlet 框架,它非常忠实地模拟了 REST 的概念。Roy Fielding 的USC dissertation定义的 REST 原则也可能会有所帮助。

4
很抱歉,吉姆,这是完全错误的。媒体类型正是用来“传达数据细节”的。媒体类型是与客户端的合约的一部分。 - Darrel Miller
6
抱歉,Darrel,你需要阅读RFC2046(http://tools.ietf.org/html/rfc2046),并查看IANA注册表(www.iana.org)中的注册媒体类型。 媒体类型类似于文件格式,而不是文件内容。 当然,“vnd”命名空间可供任何人(误)使用。 在REST设计中,资源的表现形式都有相关联的媒体类型。在OP的方法中,只有一种表现形式是可能的。“互联网媒体类型……是用于互联网上文件格式的两部分标识符。” - Jim Ferrans
2
Roy Fielding最近表示:“REST API应该在定义用于表示资源的媒体类型方面花费几乎所有的描述性工作...” 在标准格式和自定义格式之间做出选择是一个困难的选择,但application/xml几乎是您可以做出的最糟糕的选择。基本上唯一使其工作的方法是在能够解释xml格式的代码下载浏览器中。但这种选择使重用非常困难。 - Darrel Miller
2
@Wahnfrieden:Sam Ruby观察到:“并不是要从Roy(有效的)批评中削弱任何东西[请参见@Darrel的链接],REST并不是全有或全无的命题。部分采用也可以获得重要价值。”请参阅http://www.intertwingly.net/blog/2008/10/21/Progressive-Disclosure - Jim Ferrans
1
@JimFerrans 很容易争论说全面采用可以获得更多的价值。 - Eric Elliott
显示剩余6条评论

2

媒体类型应该很少被创建,并且需要投入时间确保格式能够适应变化。

由于您依赖xml,因此没有特殊原因不能创建一个媒体类型,只要该媒体类型在一个源中进行了描述。

选择ATOM而不是拥有一个支持多个根元素的主机媒体类型并不能为您带来什么:在决定是否存在足够信息来处理请求之前,仍然需要在特定操作的上下文中开始阅读消息。

因此,我建议您可以拥有一个媒体类型,由一个根元素表示,并使用模式语言指定可以包含哪些元素。

换句话说,像xsd这样的语言可以让您键入媒体类型以支持多个根元素中的一个。 application/vnd.acme.humanresources+xml没有任何固有问题,可以描述一个可以将 或 作为根元素之一的xml文档。

因此,为了回答您的问题,请尽可能少地创建媒体类型,通过质疑您放入媒体类型文档中的内容是否可被开发人员理解和实现来确定。


1
使用ATOM feed作为媒体类型来管理你的集合唯一的优点是,你可以使用任何旧的RSS阅读器查看集合中新增的内容。也许这不是一个有用的要求,但它可能会有所帮助。Feed阅读器无法对实际的ATOM条目进行太多操作,但标题可以用作简短描述。 - Darrel Miller
你说得很对,但这意味着在你的解决方案中导入另一种媒体类型。我通常对过度重用ATOM持谨慎态度,我可以看到滥用封装格式会带来很多痛苦... - SerialSeb

0

除非您打算注册这些媒体类型,否则您应该选择现有的MIME类型之一,而不是尝试创建自己的格式。正如Jim所提到的application/xml或text/xmlapplication/json适用于在REST设计中传输的大多数内容。

回复Darrel,这里是Roy的完整帖子。您难道不是通过创建自己的MIME类型来定义类型化资源吗?

Suresh,为什么HTTP+POX不符合RESTful?


3
根据目标受众需要标准化媒体类型。如果你想覆盖全球,最好使用现有注册的媒体类型。如果你的应用程序不会超越企业防火墙,则在企业内部标准化自定义媒体类型比使用application/xml更好。用Roy Fielding的话说,“REST API不应该具有对客户端重要的“有类型”的资源。唯一对客户端重要的是当前表示形式的媒体类型......”。Application/xml没有太大帮助。 - Darrel Miller
抱歉,保罗,我不认为HTTP+POX符合RESTful的标准。 - Suresh Kumar
2
保罗问:“你不是在试图通过创建自己的MIME类型来定义类型化资源吗?” 是的。这就是为什么Roy说:“对客户端有意义的唯一类型是当前表示的媒体类型”。发送application/xml,然后让客户端进入该XML并将其反序列化为客户端上的具体类型会引入隐藏的耦合。 “自描述”REST约束实际上意味着客户端不能比媒体类型告诉它更多地了解表示。Application/xml只是说您有元素和属性! - Darrel Miller
1
Mark Baker撰写了多篇文章,解释为什么XML Schema不能替代媒体类型http://www.markbaker.ca/blog/2004/09/why-namespaces-dont-replace-media-types/。 - Darrel Miller
2
另一个问题是客户端必须在能够处理消息之前解析实体主体。 - Darrel Miller
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接