RESTful集合资源 - 惯用的JSON表示和往返

时间:2012-09-21 11:20:03

标签: json api http rest odata

我有一个名为Columns的集合资源。一个GET Accept: application/json can't directly return a collection,所以我的代表需要将其嵌套在一个属性中: -

{ "propertyName": [
   { "Id": "Column1", "Description": "Description 1" },
   { "Id": "Column2", "Description": "Description 2" }
  ]
}

问题:

  1. 用于上述标识符 propertyName 最佳名称是什么?应该是:

    • d (即 d 既定惯例还是特定于某些特定框架(MS WCFMS ASP.NET AJAX?)
    • 结果(即结果是既定惯例还是特定于某些特定规范(MS OData)?)
    • (即顶级属性应具有明确的名称,这有助于消除我对通用application/json的使用的歧义作为媒体类型)

    NB我觉得应该有一些包装它的东西很舒服,正如@tuespetre指出的那样,XML或任何其他表示方式会迫使你将它包装到某种程度

  2. 当回放内容时,是否应该保留所述属性中的相同包装[鉴于出于安全原因它实际上并不是必需的,并且可能传统的JSON使用惯用法可能会丢弃这样的嵌套用于PUT和POST,因为它们是“没有必要防范脚本攻击”?

    我的直觉告诉我它应该是对称的,因为每个其他的表示,但可能有先前的艺术放弃 d / *结果** [假设这是答案第1部分* *

    ...或者PUT-back(或POST)是否需要包装属性,只需使用: -

     [
           { "Id": "Column1", "Description": "Description 1" },
           { "Id": "Column2", "Description": "Description 2" }
     ]
    
    • 如果有人希望添加,那么任何根级元数据会在哪里?
    • 一个人如何制作一个POST只是知道它需要是对称的吗?

  3. 编辑:我特别感兴趣的答案是基于合理的理由,特别考虑到JSON对客户端使用的影响。例如,HAL负责定义对两个目标表示都有意义的绑定。

    编辑2:还没有接受,为什么?到目前为止,答案没有引用或任何使他们在我搜索和挑选前20个点击中的东西突出的东西看似合理。我太挑剔了吗?我想我是(或者更有可能我不能正确地提出问题:D)。它有点疯狂,一周和3天,即使有一个公认的微不足道的奖金仍然只能获得123次观看(其中3个答案也不错)

3 个答案:

答案 0 :(得分:3)

更新答案

解决你的问题(而不是在我的原始答案中略微相切:D),这是我的观点:

1)我对此的主要看法是我不喜欢d。作为使用API​​的客户端,我会发现它令人困惑。无论如何它甚至代表什么?数据?

其他选项看起来不错。 Columns很好,因为它会向用户反映他们所要求的内容。

如果您正在进行分页,那么另一个选项可能是pageslice,因为它向客户明确表示他们没有收到集合的全部内容。

{
    "offset": 0,
    "limit": 100,
    "page" : [
        ...
    ]
}
2)TBH,我不认为这会给你带来多大的改变,不过如果是我的话,我可能不会费心收回信封,因为我没有&#39 ;认为有任何需要(见下文)以及为什么要求结构比它需要的更复杂?

我认为回信封将是奇怪的。 POST应该允许你将项目添加到集合中,那么为什么客户需要发布信封才能执行此操作?

从RESTful的角度来看,将信封放回去是有意义的,因为它可以被视为更新与整个集合相关的元数据。我认为值得思考一下你将在信封中公开的那种元数据。我认为所有适合这个信封的东西(如分页,聚合,搜索方面和类似的元数据)都是只读的,所以客户端将其发送回服务器是没有意义的。如果您发现自己在信封中有大量数据,客户端能够变异 - 那么它可能是一个标志,将该数据分解为一个单独的资源,列表作为子集合。垃圾的例子:

/animals

{
    "farmName": "farm",
    "paging": {},
    "animals": [
        ...
    ]
}

可以分解为:

/farm/1

{
    "id": 1,
    "farmName": "farm"
}

/farm/1/animals

{
    "paging": {},
    "animals": [
        ...
    ]
}

注意:即使进行了这种拆分,您仍然可以使用Facebook'sLinkedIn's字段扩展语法等方式将两者合并为单个响应。例如。 http://example.com/api/farm/1?field=animals.offset(0).limit(10)

作为回应,关于客户端应该如何知道他们正在POST和PUTing的JSON有效负载应该是什么样的问题 - 这应该反映在您的API文档中。我不确定是否有更好的工具,但Swagger提供的规范允许您使用JSON Schema记录您的请求正文应该是什么样子 - 请查看{{3} }如何定义模式,this page如何将它们作为body类型的参数引用。不幸的是,Swagger还没有将请求主体可视化为它的花哨网页用户界面,但它是开源的,所以你总是可以添加一些东西来做到这一点。

原始答案

在该页面的讨论主题中查看this page评论 - 他建议一种完全避免利用漏洞的方法,这意味着您可以安全地在响应的根目录中使用JSON数组,然后您不必担心你们两个问题。

您链接的漏洞使用Cookie来依赖您的API来验证用户的会话 - 只需使用查询字符串参数即可删除漏洞。无论如何,这可能值得这样做,因为在API上使用Cookie进行身份验证并不是非常RESTful - 您的某些客户可能不是Web浏览器,可能不想处理Cookie。

为什么这个修复工作?

漏洞攻击是一种CSRF攻击形式,它依赖于攻击者能够在他/她自己的页面上向您的API上的敏感资源添加script标记。

<script src="http://mysite.com/api/columns"></script> 

受害者网络浏览器会将mysite.com下存储的所有Cookie发送到您的服务器和服务器,这看起来像是合法请求 - 您将检查session_id Cookie(或任何服务器端)框架调用cookie)并查看用户是否经过身份验证。请求将如下所示:

GET http://mysite.com/api/columns
Cookie: session_id=123456789;

如果您更改API而忽略Cookie并使用session_id查询字符串参数,攻击者将无法欺骗受害者网络浏览器将session_id发送到您的API。

现在,有效请求将如下所示:

GET http://mysite.com/api/columns?session_id=123456789

如果使用JavaScript客户端发出上述请求,您可以从cookie获取session_id。使用其他域中的JavaScript的攻击者将无法执行此操作,因为您无法获取其他域的Cookie( William's )。

现在我们已修复此问题并忽略了session_id cookie,攻击者网站上的脚本标记仍会发送类似的请求,其中包含以下GET行:

GET http://mysite.com/api/columns

但是您的服务器将以403 Forbidden响应,因为GET缺少必需的session_id查询字符串参数。

如果我未对此API的用户进行身份验证,该怎么办?

如果您没有对用户进行身份验证,那么您的数据可能不敏感,任何人都可以调用URI。 CSRF应该是一个非问题,因为没有身份验证,即使您阻止CSRF攻击,攻击者也可以调用您的API服务器端获取您的数据并以他/她想要的方式使用它。

答案 1 :(得分:2)

  1. 我会选择'd',因为它清楚地将资源的“信封”与其内容区分开来。这也使消费者更容易解析您的回复,而不是“猜测”给定资源的包装属性的名称,然后才能访问它所拥有的内容。

  2. 我认为你在谈论两件不同的事情:

    • POST请求应在application/x-www-form-urlencoded中发送。如果您选择在回复中包含新创建的资源的表示,则您的响应应该基本上镜像GET。 (在HTTP中不是强制性的。)
    • PUT绝对应该与GET对称。 PUT请求的目的是将现有资源表示替换为另一个。让两个请求共享相同的约定是有意义的,不是吗?

答案 2 :(得分:1)

  1. 使用'Columns',因为它在语义上有意义。它有助于思考JSON和XML如何相互映射。

  2. 如果您要将该集合丢回,您可以使用相同的媒体类型(语法,格式,您将其称之为。)