Ind*_*von 9 rest json api-design http
当前,我们正在为系统开发API,并且某些资源可能具有不同的标识符。例如,有一个名为的资源orders,该资源可能具有唯一的订单号,也具有唯一的ID。目前,我们只有id的URL,这些URL是:
GET /api/orders/{id}
PUT /api/orders/{id}
DELETE /api/orders/{id}
Run Code Online (Sandbox Code Playgroud)
但是现在我们还需要使用订单号的可能性,通常会导致:
GET /api/orders/{orderNumber}
PUT /api/orders/{orderNumber}
DELETE /api/orders/{orderNumber}
Run Code Online (Sandbox Code Playgroud)
显然,这是行不通的,因为id和orderNumber都是数字。
我知道也有一些类似的问题,但是它们并不能帮助我,因为答案并不合适,或者他们的方法不是真正令人烦恼或难以理解的(对于我们以及使用API的开发人员而言)。此外,问题和答案的年龄部分超过7年。
仅举几例:
1.使用查询参数
一个建议使用查询参数,例如
GET /api/orders/?orderNumber={orderNumber}
Run Code Online (Sandbox Code Playgroud)
我认为有很多问题。首先,这是订单集合的过滤器,因此结果也应该是列表。但是,唯一的订单号只有一个订单,这有点令人困惑。其次,我们使用这种过滤器来搜索/过滤订单的子集。此外,查询参数是某种第二类参数,但在这种情况下应为第一类。如果我的对象不存在,这甚至是一个问题。通常GET /api/orders/?orderNumber=1234,如果顺序1234不存在,则get将返回404(未找到),但是a 将为空数组。
2.使用前缀
一些公共API使用某种区分符来区分不同类型,例如:
GET /api/orders/id_1234
GET /api/orders/ordernumber_367652
Run Code Online (Sandbox Code Playgroud)
这对于他们的方法是有效的,因为id_1234和ordernumber_367652是它们的真正唯一标识符,其他资源也返回它们。但是,这将导致这样的响应对象:
{
"id": "id_1234",
"ordernumber": "ordernumber_367652"
//...
}
Run Code Online (Sandbox Code Playgroud)
这不是很干净,因为类型(标识或订单号)已建模两次。而且,除了更改所有标识符和响应对象的问题外,如果您想搜索所有大于67363的订单号(因此,还存在字符串/数字冲突),这将令人困惑。如果响应未将类型添加为前缀,则用户必须为某些请求添加此类型,这也将非常令人困惑(有时您必须添加此类型,有时不添加...)
3.使用动词
例如Twitter就是这样的:其URL以结尾show.json,因此您可以像这样使用它:
GET /api/orders/show.json?id=1234
GET /api/orders/show.json?number=367652
Run Code Online (Sandbox Code Playgroud)
我认为,这是最糟糕的解决方案,因为它不会带来麻烦。此外,它还存在我在查询参数方法中提到的一些问题。
4.使用子资源
有人建议像子资源一样对此建模,例如:
GET /api/orders/1234
GET /api/orders/id/1234 //optional
GET /api/orders/ordernumber/367652
Run Code Online (Sandbox Code Playgroud)
我喜欢这种方法的可读性,但是我认为的意思/api/orders/ordernumber/367652是“获取(仅)订单号367652”,而不是订单。最后,这打破了一些最佳做法,例如使用复数形式和仅使用实际资源。
所以最后,我的问题是:我们错过了什么吗?还有其他方法,因为我认为这不是一个罕见的问题吗?
对我来说,解决问题的最 RESTful 方法是使用方法 2 稍加修改。
从理论上讲,您只需拥有有效的识别码来识别您的订单。在设计过程的这一点上,您的识别码是 ID 还是订单号并不重要。这是唯一标识您的订单的东西,这就足够了。
您在 ids 和数字格式之间存在歧义的事实是属于实现阶段的问题,而不是设计阶段。
所以现在,我们所拥有的是:
GET /api/orders/{some_identification_code}
这是非常 RESTful 的。
当然,您仍然有解决歧义的问题,因此我们可以继续实施阶段。不幸的是,您的订单identification_code集由两个共享格式的不同实体组成。这是微不足道的,它无法工作。但现在问题出在这些实体格式的定义上。
我的建议很简单:id 将是整数,而数字将是代码,例如 N1234567。这种方法将使您的资源表示可以接受:
{
"id": "1234",
"ordernumber": "N367652"
//...
}
Run Code Online (Sandbox Code Playgroud)
此外,它在许多场景中很常见,例如快递运输。
这是我想出的另一种选择,我发现它稍微更容易接受。
GET /api/orders/1234
GET /api/orders/1234?idType=id //optional
GET /api/orders/367652?idType=ordernumber
Run Code Online (Sandbox Code Playgroud)
原因是它使路径与 REST 标准保持一致,然后在服务中,如果它们确实传递了 idType=orderNumber (id 的 idType 是默认值),您就可以了解这一点。
我也在为此苦苦挣扎!就我而言,我只需要能够使用辅助 ID 获取,这使得这变得更容易一些。
我倾向于在 ID 中使用可选前缀:
GET /api/orders/{id}
GET /api/orders/id:{id}
GET /api/orders/number:{orderNumber}
Run Code Online (Sandbox Code Playgroud)
或者这可能是一个使用 URI 规范的一个不起眼的功能的机会,即路径参数,它允许您将参数附加到特定的路径元素:
GET /api/orders/{id}
GET /api/orders/{id};id_type=id
GET /api/orders/{orderNumber};id_type=number
Run Code Online (Sandbox Code Playgroud)
使用不合格 ID 的 URL 是规范的 URL。非规范 URL 的行为有两种选择:返回实体,或重定向到规范 URL。后者理论上更纯粹,但对于用户来说可能不方便。或者它可能对用户更有用,谁知道呢!
解决此问题的另一种方法是将订单号建模为其自身的事物:
GET /api/ordernumbers/{orderNumber}
Run Code Online (Sandbox Code Playgroud)
这可以返回一个仅包含 ID 的小对象,然后用户可以使用该对象来检索实体。或者甚至只是重定向到订单。
如果您还想要通用搜索资源,那么也可以在这里使用:
GET /api/orders?number={orderNumber}
Run Code Online (Sandbox Code Playgroud)
就我而言,我(还)不想要这样的资源,而且我可能会不愿意添加似乎只支持一个字段的通用搜索资源。
我正在为同样的问题而苦苦挣扎,但尚未找到完美的解决方案。我最终使用了这种格式:
GET /api/orders/{orderid}
GET /api/orders/bynumber/{orderNumber}
Run Code Online (Sandbox Code Playgroud)
不完美,但可读。
| 归档时间: |
|
| 查看次数: |
1729 次 |
| 最近记录: |