【问题标题】:REST Media type explosionREST 媒体类型爆炸
【发布时间】:2009-05-19 03:47:42
【问题描述】:

在尝试使用 REST 架构风格重新设计现有应用程序时,我遇到了一个我想称之为“媒体类型爆炸”的问题。但是,我不确定这是否真的是一个问题或 REST 的固有优势。为了解释我的意思,举个例子

我们应用程序的一小部分如下所示:

collection-of-collections->collections-of-items->items

即顶层是集合的集合,每个集合又是项目的集合。

此外,每个项目都有 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. 有什么替代设计方法可以避免这种媒体类型的爆炸式增长?

我也知道上面的设计是高度精细的,尤其是暴露了项目的各个属性并为每个属性设置了单独的媒体类型。然而,粗化意味着我最终会通过网络传输不必要的数据,而实际上客户端只需要读取或写入项目的单个属性。您将如何处理这样的设计问题?

【问题讨论】:

    标签: rest


    【解决方案1】:

    减少所需媒体类型数量的一种方法是使用定义的媒体类型来保存其他媒体类型的列表。这可以用于您的所有收藏。通常,列表往往具有一致的行为集。 你可以滚动你自己的 vnd.mycompany.resourcelist 或者你可以重用类似 Atom collection 的东西。

    对于像 vnd.mycompany.item 这样的特定资源表示,您可以做什么很大程度上取决于您的客户的特征。它在浏览器中吗?你能做代码下载吗?您的客户端是富 UI,还是数据处理客户端?

    如果客户要进行特定的数据处理,那么您几乎需要坚持使用精确的媒体类型,最终您可能会得到大量的媒体类型。但是从好的方面来看,如果您使用 SOAP,您将拥有的媒体类型将少于名称空间!

    请记住,媒体类型是您的合同,如果您的应用程序需要与客户端定义大量合同,那就这样吧。

    但是,我不会定义合约来交换单个属性值。如果您觉得有必要这样做,那么您在设计中做错了其他事情。分布式界面设计需要有大量的对话,而不是闲聊。

    【讨论】:

    • 达雷尔,感谢您的宝贵意见。目前,我确实看到需要定义合约以在我们的应用程序中交换单个属性值。但你是对的,也许我需要在这里采取不同的方法......
    【解决方案2】:

    我想我终于从Ian Robinson's presentation 得到了我为上述问题寻求的澄清,并认为我应该在这里分享。

    最近,我在Jim Webber 的博客文章中看到了“用于帮助调整超媒体引擎的媒体类型,结构的模式”的声明。然后我在 Thoughtworks 的Ian Robinson 上找到了这个presentation。这个演示文稿是我遇到的最好的演示文稿之一,它提供了对媒体类型和模式语言的角色和责任的非常清晰的理解(整个演示文稿是一种享受,我强烈推荐给所有人)。尤其要注意标题为“你选择了应用程序/xml,你bstrd”的幻灯片。和“自定义媒体类型”。 Ian 清楚地解释了模式和媒体类型的不同角色。简而言之,这是我对 Ian 演讲的总结:

    媒体类型描述包括识别超媒体控件并定义适用于该类型资源的方法的处理模型。识别超媒体控件意味着“我们如何识别链接?”在 XHTML 中,链接是基于标签来识别的,而 RDF 有不同的语义。媒体类型帮助识别的下一件事是哪些方法适用于给定媒体类型的资源?一个很好的例子是 ATOM (application/atom+xml) 规范,它对超媒体控件提供了非常丰富的描述;他们告诉我们链接元素是如何定义的?以及当我们取消引用 URI 时我们可以期望能够做什么,因此它实际上告诉了我们可以期望能够应用于资源的方法。资源表示的结构信息是部分或包含在媒体类型描述中,但作为实际表示的适当模式的一部分提供,即媒体类型规范不一定会对表示的结构做出任何规定。

    那么这对我们意味着什么?只是我们不需要单独的媒体类型来描述我在原始问题中描述的每个资源。整个应用程序只需要一种媒体类型。这可能是一种全新的自定义媒体类型,也可以是重用现有标准媒体类型的自定义媒体类型,或者更好的是,只是一种可以在我们的应用程序中重用而无需更改的标准媒体类型。

    希望这会有所帮助。

    【讨论】:

    • 这种媒体类型的一个很好的例子是HAL。它同时支持jsonxml,并在涉及超媒体驱动的API 时尽可能注重实用性。
    • @edsioufi - 是的,我们实际上已经开始在我们的一个项目中同时使用 XHMTL 和 HAL+JSON,到目前为止似乎已经完成了工作。
    • @edsioufi 好点。发现这个HAL primer 有助于理解application/hal+json 的用法。
    【解决方案3】:

    在我看来,这是 REST 概念的薄弱环节。作为一种架构和界面风格,REST 非常出色,Roy F. 和其他人所做的工作大大提高了最新技术水平。但是标准媒体类型可以传达(不仅仅是表示)的内容有一个上限。

    为了让人们了解和使用您的 REST 式 API,他们需要了解数据的含义。有媒体类型讲述大部分故事的 API;例如如果你有一个文本到语音的 API,输入媒体类型是 text/plain,输出媒体类型是音频/mp4,那么熟悉主题的人可能会做。文本输入,音频输出,在这种情况下可能就足够了。

    但许多 API 不能仅通过媒体类型来传达它们的大部分含义。假设您有一个处理机票的 API。输入和输出将主要是数据。每个 API 的输入和输出的媒体类型可以是 application/json 或 application/xml,因此媒体类型不会传输很多信息。因此,您将查看输入和输出中的各个字段。也许有一个字段叫做“价格”。是美元还是便士?美元还是其他货币?我不知道如果没有(a)非常描述性的名称,如“price_pennies_in_usd”或(b)文档,用户将如何回答这些问题。更不用说格式约定了。是提供或不提供破折号的帐号,字母必须全部大写等。没有定义这些问题的标准媒体类型。

    当我们处于客户端不需要对数据的语义理解的情况下,这是一回事。效果很好。浏览器可以直观地呈现任何兼容的文档,并与任何兼容的资源进行交互,这一事实非常棒。这基本上就是“媒体”用例。

    但是当客户端(或者实际上是客户端背后的开发人员/用户)需要理解数据的语义时,情况就完全不同了。 数据不是媒体。除了记录数据之外,没有办法解释数据的所有现实意义和微妙之处。这是“数据”用例。

    REST 的过度学术定义适用于媒体用例。它不起作用,需要补充一些不纯但有用的东西,比如文档,用于其他用例。

    【讨论】:

    • 超媒体类型的存在是为了告诉客户如何与数据交互——而不是数据的含义。例如,两种通用超媒体类型:Collection+JSON 和 Siren 都可以轻松描述集合、集合项(资源)、可以针对相关资源执行的操作以及相关链接(如 first/last/previous/next对于分页信息...)。链接为王。这是 Web 浏览器如何与 HTML 交互的粗略模拟。
    【解决方案4】:

    您正在使用媒体类型来传达应存储在表示本身中的数据的详细信息。因此,您可以只有一种媒体类型,例如“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>
    

    如果您担心发送太多数据,请用 JSON 代替 XML。另一种节省写入和读取字节数的方法是使用 gzip 编码,它可以减少大约 60-70%。除非您有超高性能的需求,否则其中一种方法应该适合您。 (为了获得更好的性能,您可以使用非常简洁的手工字符串,甚至可以使用自定义二进制 TCP/IP 协议。)

    编辑您的担忧之一是:

    使[表示]粗糙意味着我最终将通过网络传输不必要的数据,而实际上客户端只需要读取或写入项目的单个属性

    在任何 Web 服务中,发送消息都会产生相当多的开销(每个 HTTP 请求的起始行和请求标头可能花费数百字节,每个 HTTP 响应也是如此,如this example)。因此,一般来说,您希望拥有较少粒度的表示。因此,您将编写您的客户端来请求这些更大的表示,然后将它们缓存在一些方便的内存数据结构中,您的程序可以从中多次读取数据(但请确保遵守服务器设置的 HTTP 到期日期)。将数据写入服务器时,您通常会将 set 更改组合到内存数据结构中,然后将更新作为单个 HTTP PUT 请求发送到服务器。

    您应该获取 Richardson 和 Ruby 的 RESTful Web Services 的副本,这是一本关于如何设计 REST Web 服务的真正优秀的书,并且比我更清楚地解释了事情。如果您使用 Java,我强烈推荐 RESTlet 框架,它非常忠实地模拟了 REST 概念。 Roy Fielding 的 USC dissertation 定义 REST 原则也可能会有所帮助。

    【讨论】:

    • 对不起,吉姆,这绝对不正确。媒体类型正是应该用来“传达数据细节”的。媒体类型是与客户签订的合同的一部分。
    • 抱歉,Darrel,您需要阅读 RFC 2046 (tools.ietf.org/html/rfc2046) 并查看 IANA 注册中心 (www.iana.org) 中注册的媒体类型。媒体类型类似于文件格式,而不是文件内容。当然,“vnd”命名空间对任何(错误)使用都是开放的。在 REST 设计中,每个资源的表示都有一个关联的媒体类型。在 OP 的方法中,只有一种表示是可能的。 “Internet 媒体类型……是 Internet 上文件格式的两部分标识符。”
    • Roy Fielding 最近表示“REST API 应该花费几乎所有的描述性工作来定义用于表示资源的媒体类型......” 标准格式和自定义格式之间的选择是一个艰难的选择,但 application/xml 几乎是你能做出的最糟糕的选择。使其工作的唯一方法几乎是在具有可以解释 xml 格式的代码下载的浏览器中。但是这种选择使得重用变得非常困难。
    • 你说得对,我的说法并不清楚。对于公共网络上的应用程序,像 atom、rss、xhtml 这样的标准 MIME 类型绝对是要走的路。我必须承认,在谈到高休息、低休息辩论时,我有点偏执。我在 lo-rest 不是 REST 人群。正确使用超媒体是一件神奇的事情。
    • @Wahnfrieden:Sam Ruby 观察到:“无意从 Roy 对标签的(有效)批评中删除任何内容 [参见 @Darrel 的链接],REST 不是一个全有或全无的主张。人们可以得到部分采用的重要价值。”见intertwingly.net/blog/2008/10/21/Progressive-Disclosure
    【解决方案5】:

    应该很少创建媒体类型,并且应该投入时间来确保格式能够经受住变化。

    由于您依赖于 xml,没有什么特别的原因不能创建一种媒体类型,前提是该媒体类型在一个来源中进行了描述。

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

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

    换句话说,像 xsd 这样的语言可以让您键入媒体类型以支持多个根元素之一。 application/vnd.acme.humanresources+xml 描述一个可以采用或作为根元素的 xml 文档本身并没有错。

    因此,要回答您的问题,请创建尽可能少的媒体类型,方法是询问您在媒体类型文档中放入的内容是否可以被开发人员理解和实施。

    【讨论】:

    • 使用 ATOM 提要作为您收藏的媒体类型的唯一优势是,您可以使用任何旧的 RSS 阅读器来查看收藏中新添加的项目。也许这不是一个有用的要求,但它可能是。提要阅读器无法对实际的 ATOM 条目做很多事情,但标题可以用作简短描述。
    • 您说的很对,但这意味着在您的解决方案中导入另一种媒体类型。我通常对过多地重用 ATOM 持谨慎态度,我可以看到滥用封装格式带来的很多痛苦......
    【解决方案6】:

    除非您打算注册这些媒体类型,否则您应该选择existing mime types 之一,而不是尝试编写自己的格式。正如 Jim 提到的,application/xml or text/xmlapplication/json 适用于 REST 设计中传输的大部分内容。

    在这里回复达雷尔的是Roy's full post。您不是想通过创建自己的 mime 类型来定义类型化资源吗?

    Suresh,为什么 HTTP+POX 不是 Restful?

    【讨论】:

    • 媒体类型需要根据其目标受众进行标准化。如果你想走向世界,那么你最好使用现有的注册媒体类型。如果您的应用程序没有到达企业防火墙之外,那么在您的企业内标准化的自定义媒体类型比 application/xml 好得多。用 Roy Fielding 的话来说,“REST API 永远不应该有对客户端很重要的‘类型化’资源。对客户端重要的唯一类型是当前表示的媒体类型......”。 application/xml 不是很有帮助。
    • 抱歉 Paul,我不考虑 HTTP+POX RESTful。
    • Paul 问道“你不是想通过创建自己的 mime 类型来定义类型化资源吗?”是的。这就是为什么 Roy 说“唯一对客户重要的类型是当前表示的媒体类型”。发送 application/xml 然后让客户端访问该 XML 并将其反序列化为客户端上的具体类型会引入隐藏耦合。 “自我描述” REST 约束实际上表示客户端无法比媒体类型了解更多关于表示的信息。 application/xml 只是说你有元素和属性!
    • Mark Ba​​ker 有很多帖子解释了为什么 XML Schema 不能替换媒体类型 markbaker.ca/blog/2004/09/…
    • 另一个问题是客户端必须先解析实体主体才能决定是否可以处理消息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-15
    • 2016-03-01
    • 2011-01-31
    • 1970-01-01
    • 2010-12-18
    • 2016-10-16
    • 1970-01-01
    相关资源
    最近更新 更多