Java 对于需要返回两组不同响应的HTTP GET REST响应,正确的设计原则是什么

Java 对于需要返回两组不同响应的HTTP GET REST响应,正确的设计原则是什么,java,rest,server,Java,Rest,Server,我想了解在按照REST设计HTTP GET响应时应遵循的正确方法。我有以下要求 class Employee { private long employeedID; private String name; private Date dob; private String address; private String department; } 根据REST建模,httpget/employees将返回所有雇员的数组。类似地,HTTP GET/employees

我想了解在按照REST设计HTTP GET响应时应遵循的正确方法。我有以下要求

class Employee {
   private long employeedID;
   private String name;
   private Date dob;
   private String address;
   private String department;
}
根据REST建模,httpget/employees将返回所有雇员的数组。类似地,HTTP GET/employees/1将返回Id为1的员工

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

我正在评估以下选项

方法1:

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

方法2:

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

方法3:

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

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

在这三种方法中,哪种方法与其他方法最接近


谢谢

我认为方法2领域的某种东西是实现这一目标的方法。从根本上说,您仍然在访问相同的搜索和结果集,并且它是相同的资源(员工)。因此,方法3并不真正合适

也就是说,有各种各样的方法可以进行。一种方法是使用一个表示投影的查询字符串参数—有点像SQL投影。比如:

GET /employees?fields=ID,name

我使用过一些这样工作的API,它们工作得很好。

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

所以,如果你想要一个最大的REST的API,考虑下面的,遵循以下标准: 这:

收益率:

[ {
    "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"
    } ]
} ]
{
    "employeeID": 1,
    "name": "Joe",
    "_links": [ {
        "rel": "self",
        "href": "http://foo.bar/employees/1"
    }, {
        "rel": "details",
        "href": "http://foo.bar/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"
    } ]
}
链接如下:

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"
    } ]
}, {
    "employeeID": 2,
    "name": "Sam",
    "_links": [ {
        "rel": "self",
        "href": "http://foo.bar/employees/2"
    }, {
        "rel": "details",
        "href": "http://foo.bar/employees/2/details"
    } ]
} ]
{
    "employeeID": 1,
    "name": "Joe",
    "_links": [ {
        "rel": "self",
        "href": "http://foo.bar/employees/1"
    }, {
        "rel": "details",
        "href": "http://foo.bar/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"
    } ]
}
以下是另一个链接:

HTTP GET /employees/1/details
收益率:

[ {
    "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"
    } ]
} ]
{
    "employeeID": 1,
    "name": "Joe",
    "_links": [ {
        "rel": "self",
        "href": "http://foo.bar/employees/1"
    }, {
        "rel": "details",
        "href": "http://foo.bar/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"
    } ]
}

想要获得灵感,请查看可能是我见过的最好的答案。

这个答案是在下面的评论中作为讨论的后续添加的

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

  • 在IMO中,当涉及到端点的数量时,它倾向于消除合理的限制。似乎一些开发人员的答案是,它是否会是RESTful。。。?或者在休息时怎么做。。。?通常是:添加一个新的端点
    /data/{id}/Add/
    ,它将通过
    \u链接
    元字段进行记录。这不是应该怎么做的。通过这种方式,您可以始终添加一个新端点和适当的链接,并以大量没有人能够理解或验证的端点结束。例如,此链接返回基本数据集:

    "http://foo.bar/employees/1"
    
    这将返回更多详细信息:

    "http://foo.bar/employees/1/details"
    
    如果我需要其他细节的子集呢?我要添加一个新端点吗?和。。如果有多个不同的客户端需要相互排斥的 数据子集?每个客户端都有一个专用的端点?这是一场噩梦

  • 链接不仅与URL有关,还与查询参数有关。它们是否包含在链接中?以什么形式?模板?因此,我无法按原样跟踪链接。每个参数都有一个默认值?我想为每个查询参数提供一个默认值是不可能的

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

  • 找时间看看谈话,这是值得的

  • 在这篇关于HATEOAS的短文之后

    方法#1


    当资源的版本发生更改(添加或删除新字段)时,应使用标题,而不需要特定字段或资源的子集。所以在我看来,这不是一条路

    方法#3

    这是一个完全不好的主意,因为在这个答案的介绍中提到的原因

    方法#2

    我知道这是一条路要走。正如@leeor所回答的,这是一种流行的、被接受的、灵活的模式


    您可以通过添加名为
    视图
    的查询参数来扩展它,该参数是一个枚举(
    简单
    扩展
    完整
    ),表示预定义视图的列表。这是避免添加新端点的方法。而是添加并记录(!)新视图。如果
    视图
    字段
    是互斥的,或者它们的处理顺序由您决定。

    查询字符串是解决方法。这是放置过滤参数的一般位置Swell,我投票表决这里讨论的#1内容协商:我错过了更新,目前我们产品中的REST API符合Richardson的2级;s成熟度模型。我理解这会取消API被称为真正RESTful的资格。没有计划