7

我有一个名为 Columns 的集合资源。A GETwithAccept: application/json 不能直接返回一个集合,所以我的表示需要将它嵌套在一个属性中:-

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

问题:

  1. 上面的标识符propertyName使用的最佳名称是什么?应该是:

    • d(即d是既定约定还是特定于某些特定框架(MS WCFMS ASP.NET AJAX?)
    • 结果(即结果是既定惯例还是特定于某些特定规范(MS OData)?)
    • (即顶级属性应该有一个清晰的名称,它有助于消除我将泛型application/json用作媒体类型的歧义)

    注意,我觉得应该有一些东西来包装它,并且正如@tuespetre 所指出的那样,XML 或任何其他表示形式无论如何都会迫使你在某种程度上包装它

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

    我的直觉告诉我,它应该与其他所有表示一样对称,但可能存在删除d / *results** 的现有技术 [假设这是第 1 部分的答案]*

    ...或者是否应该 PUT-back(或 POST)放弃对包装属性的需求,然后使用:-

    [
           { "Id": "Column1", "Description": "描述 1" },
           { “Id”:“Column2”,“描述”:“描述 2”}
     ]
    
    • 如果希望添加根级元数据,它会去哪里?
    • 制作 POST 的人如何/将只知道它需要是对称的?

编辑:我对一个具有合理理由的答案特别感兴趣,该理由特别考虑了 JSON 对客户端使用的影响。例如,HAL会注意定义对两种目标表示都有意义的绑定。

编辑2:尚未接受,为什么?到目前为止,答案没有引文或任何使它们在我搜索并从似乎合理的前 20 首热门歌曲中脱颖而出的东西。我是不是太挑剔了?我想我是(或者更有可能我只是无法正确提问:D)。有点生气的是一周零三天,即使有一个)不可否认的微不足道的奖金仍然只有 123 次观看(其中 3 个答案还不错)

4

3 回答 3

3

更新的答案

解决您的问题(而不是在我的原始答案中有点切线:D),这是我的意见:

1)我对此的主要看法是我不喜欢d. 作为使用 API 的客户,我会觉得这很混乱。它到底代表什么?数据?

其他选项看起来不错。Columns很好,因为它反映了用户的要求。

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

{
    "offset": 0,
    "limit": 100,
    "page" : [
        ...
    ]
}

2) TBH,我认为你选择哪种方式并没有太大的不同,但是如果是我,我可能不会费心寄回信封,因为我认为没有任何必要(见下面)以及为什么使请求结构比它需要的更复杂?

我认为邮寄信封会很奇怪。POST 应该允许您将项目添加到集合中,那么为什么客户端需要发布信封来执行此操作?

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

/animals

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

可以分解为:

/farm/1

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

/farm/1/animals

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

注意:即使使用此拆分,您仍然可以使用FacebookLinkedIn 的字段扩展语法将两者组合为单个响应返回。例如http://example.com/api/farm/1?field=animals.offset(0).limit(10)

作为回应,您的问题是客户端应该如何知道他们发布和放置的 JSON 有效负载应该是什么样子 - 这应该反映在您的 API 文档中。我不确定是否有更好的工具来解决这个问题,但Swagger提供了一个规范,允许您使用JSON Schema记录您的请求主体应该是什么样子- 查看此页面了解如何定义您的模式,并查看此页面了解如何将它们作为类型的参数引用body。不幸的是,Swagger 还没有在其精美的 Web UI 中可视化请求主体,但它是开源的,所以你总是可以添加一些东西来做到这一点。

原始答案

查看William在该页面的讨论线程中的评论 - 他提出了一种完全避免漏洞利用的方法,这意味着您可以安全地在响应的根目录中使用 JSON 数组,然后您就不必担心你们中的任何一个问题。

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

为什么此修复程序有效?

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

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

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

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

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

一个有效的请求现在看起来像这样:

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

如果使用 JavaScript 客户端发出上述请求,您可以从 cookie 中获取 session_id。使用来自另一个域的 JavaScript 的攻击者将无法执行此操作,因为您无法获取其他域的 cookie(请参见此处)。

现在我们已经解决了这个问题并且忽略了 session_id cookie,攻击者网站上的脚本标签仍然会发送一个类似的请求,其中包含这样的 GET 行:

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

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

如果我不为此 API 验证用户怎么办?

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

于 2012-09-26T20:45:48.673 回答
2
  1. I would go for 'd' because it clearly separates the 'envelope' of your resource from its content. This would also make it easier for consumers to parse your responses, as opposed to 'guessing' the name of the wrapping property of a given resource before being able to access what it holds.

  2. I think you're talking about two different things:

    • POST request should be sent in application/x-www-form-urlencoded. Your response should basically mirror a GET if you choose to include a representation of the newly created resource in your reply. (not mandatory in HTTP).
    • PUTs should definitely be symmetric to GETs. The purpose of a PUT request is to replace an existing resource representation with another. It just makes sense to have both requests share the same conventions, doesn't it?
于 2012-09-29T18:27:08.750 回答
1
  1. 使用“列”,因为它在语义上是有意义的。考虑一下 JSON 和 XML 如何相互镜像会有所帮助。

  2. 如果您要将集合放回原处,您不妨使用相同的媒体类型(语法、格式、您将如何称呼它。)

于 2012-09-22T07:00:03.300 回答