【问题标题】:What is the correct design principle for HTTP GET responses in REST that need to return 2 different sets of responsesREST中需要返回2组不同响应的HTTP GET响应的正确设计原则是什么
【发布时间】:2015-10-14 02:36:48
【问题描述】:

我想了解在根据 REST 设计 HTTP GET 响应时应遵循哪种正确方法。我有以下要求

class Employee {
   private long employeedID;
   private String name;
   private Date dob;
   private String address;
   private String department;
}

按照 REST 建模,HTTP GET /employees 将返回所有员工的数组。同样 HTTP GET /employees/1 将返回 ID 为 1 的员工

现在有一个 UI 驱动的工作流,我只需要显示每个员工的 name 和 employeeID。因此,来自 HTTP GET /employees 的现有响应是重量级的(其他字段被不必要地传输)。因此,我想将响应限制为仅包含每个员工的 name 和 employeeID。

我正在评估以下选项

方法一:

使用 Content-Type HTTP 标头指示发出 HTTP GET /employees 请求的客户端需要在响应中修剪属性列表。即在 Content-Type 中有一些自定义字符串(即 application.summary+json),这将导致响应中仅包含 2 个属性

方法二:

使用额外的查询参数作为 HTTP GET /employess?isSummary=true。在这种情况下,在服务器端,根据 isSummary 参数的值,我只能返回每个员工的 2 个属性

方法 3:

创建一个支持精简响应的新 REST 端点本身,即 HTTP GET /employees/summaryDetails

在这种情况下,上述端点中只会返回 2 个属性。

在这 3 种方法中,哪种方法最接近 REST?

谢谢

【问题讨论】:

标签: java rest server


【解决方案1】:

我认为方法#2领域的东西是这里的方法。从根本上说,您仍然在访问资源(员工)方面的相同搜索和结果集,并且它是相同的资源。所以方法 #3 并不适合。

也就是说,关于#2 有多种方法。一种方法是使用一个表示投影的查询字符串参数——有点像 SQL 投影。所以像:

GET /employees?fields=ID,name

我曾使用过一些以这种方式工作的 API,它们运行良好。

【讨论】:

  • 这种机制称为资源扩展,请不要以任何方式将 REST 与 SQL 混用。这是 REST 端点应该直接返回 DB 中的内容的最常见缺陷。
【解决方案2】:

此答案是在this 答案下方的 cmets 中作为后续讨论添加的。

基本上,是的,我反对 HATEOAS。为什么?总的来说,这个想法很好,但是:

  1. IMO 在端点数量方面倾向于消除合理的限制。似乎有些开发者对Will it is RESTful...? 或How to do it in REST...? 的回答很常见:加一个新的端点/data/{id}/add/,它将通过_links 元字段进行记录。这不是应该做的。通过这种方式,您始终可以添加一个新的端点和适当的链接,并以大量没有人能够理解或验证的端点结束。例如。此链接返回基本数据集:

    "http://foo.bar/employees/1"
    

    这会返回更多细节:

    "http://foo.bar/employees/1/details"
    

    如果我需要其他详细信息子集怎么办?我会添加一个新端点吗?而且.. 如果有多个不同的客户端需要互斥 数据子集?这些客户端中的每一个都有一个专用端点吗?这是一场噩梦!

  2. 链接不仅与 URL 有关,还与查询参数有关。它们是否包含在链接中?以什么形式?模板?所以我不能按原样点击链接。每个参数都有一个默认值?我想这不可能简单地为每个查询参数提供一个默认值。

  3. 提到的可发现性和文档。相信我,对于您设计、开发和部署的大多数 API,您都需要编写文档。为什么?因为订购此 API 的公司需要它。 stripe.com 是否遵循 HATEOS 规则?不!那么为什么会如此成功呢?因为它非常好 documented 并且有库以及多种最流行的语言和工具的示例。

  4. 找时间看this的谈话,值得。

现在在这篇简短关于 HATEOAS 的说明之后。

方法#1

当资源版本发生变化(添加或删除新字段)本身而不是您需要特定字段或资源子集时,应使用标题。所以 IMO 这不是要走的路。

方法#3

由于这个答案的介绍中提到的原因,这是一个完全坏主意。

方法#2

恕我直言,这是要走的路。正如@leeor 所回答的那样,这是一种流行、被接受且灵活的模式。

您可以通过添加一个名为 e.g. 的查询参数来扩展它。 view 是一个枚举(SIMPLE、EXTENDED、FULL),表示预定义视图的列表。这是避免添加新端点的方法。相反,您添加并记录(!)新视图。 view 和 fields 是否互斥或处理它们的顺序取决于您。

【讨论】:

    【解决方案3】:

    每个列出的方法的问题在于它使 API 复杂化,并且可能违反了 REST 的最基本原则,即可发现性。响应没有提供任何上述 API 存在的线索。您将不得不阅读文档(恐怖!)。 REST 的基本规则是HATEOAS:超文本作为应用程序状态的引擎。

    因此,如果您想要一个最大程度 RESTful 的API,请考虑以下内容,它遵循称为HAL 的标准:

    这个:

    HTTP GET /employees
    

    产量:

    [ {
        "employeeID": 1,
        "name": "Joe",
        "_links": [ {
            "rel": "self",
            "href": "http://foo.bar/employees/1"
        }, {
            "rel": "details",
            "href": "http://foo.bar/employees/1/details"
        } ]
    }, {
        "employeeID": 2,
        "name": "Sam",
        "_links": [ {
            "rel": "self",
            "href": "http://foo.bar/employees/2"
        }, {
            "rel": "details",
            "href": "http://foo.bar/employees/2/details"
        } ]
    } ]
    

    点击链接:

    HTTP GET /employees/1
    

    产量:

    {
        "employeeID": 1,
        "name": "Joe",
        "_links": [ {
            "rel": "self",
            "href": "http://foo.bar/employees/1"
        }, {
            "rel": "details",
            "href": "http://foo.bar/employees/1/details"
        } ]
    }
    

    然后点击另一个链接:

    HTTP GET /employees/1/details
    

    产量:

    {
        "employeeID": 1,
        "name": "Joe",
        "dob": "1985-04-23",
        "address": "123 Main St",
        "department": "Department of Redundant Links",
        "_links": [ {
            "rel": "self",
            "href": "http://foo.bar/employees/1"
        }, {
            "rel": "details",
            "href": "http://foo.bar/employees/1/details"
        } ]
    }
    

    要获得灵感,请查看JIRA REST API,这可能是我见过的最好的。

    【讨论】:

    • 我错过了当前我们产品中的 REST API 符合 Richardson;s 成熟度模型第 2 级的更新。我知道这使 API 无法被称为真正的 RESTful。没有计划将这些更改为第 3 级(因为 UI 需要足够智能才能处理链接以进行发现)。因此,鉴于需要继续进行第 2 级,我想知道上面列出的三种方法中的最佳方法
    • IMO 这不是要走的路。资源扩展似乎是更好的选择,并且没有大量端点(每次需要为客户端添加内容时引入)甚至是可发现的。
    • @Opal,你能改写一下吗?你的意思是 3 级 (HATEOAS) 在这里有点矫枉过正吗?或者我使用三种方法中的一种仅实现第 2 级的意图是不正确的?
    • 我确实认为 HATEOAS 通常是一种过度杀伤力。稍后可以添加我自己的答案。
    猜你喜欢
    • 2019-03-29
    • 2011-02-03
    • 1970-01-01
    • 2016-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-11
    相关资源
    最近更新 更多