用于大型应用程序的RESTful API;混搭;数据拉?

用于大型应用程序的RESTful API;混搭;数据拉?,api,rest,Api,Rest,我最近接受了一个小项目的任务,即从内部数据库到第三方产品定期(每天和每周之间)转储数据。这个项目很好地符合了我的公司的愿望(我也有这个愿望),即在我们的数据之上建立一个正式的服务层/API 我个人的偏好是,这些API应该采用RESTful端点的形式——然而,现在我有一个很大的设计问题——让我解释一下 从所涉及的数据来看,这并不复杂。如果我只是想构造一个一次性查询,它在概念上看起来有点像: select o.order_num, o.order_date, p.product_descriptio

我最近接受了一个小项目的任务,即从内部数据库到第三方产品定期(每天和每周之间)转储数据。这个项目很好地符合了我的公司的愿望(我也有这个愿望),即在我们的数据之上建立一个正式的服务层/API

我个人的偏好是,这些API应该采用RESTful端点的形式——然而,现在我有一个很大的设计问题——让我解释一下

从所涉及的数据来看,这并不复杂。如果我只是想构造一个一次性查询,它在概念上看起来有点像:

select o.order_num, o.order_date, p.product_description, sr.sales_rep_name
from order o, line_item li, product p, sales_rep sr
where li.order_num = o.order_num
  and li.product_id = p.product_id
  and sr.sales_rep_id = o.sales_rep_id
  and o.order_date >= [some arbitrary date]
GET /orderReport/[some arbitrary date]
将我的大脑切换到“资源模式”,我可以考虑如何将这个基本数据模型转换为URI/有效负载,而不会有太多麻烦:

GET /orders/123
{ 
  "order_num": 59324, 
  "order_date": "2014-07-07", 
  "sales_rep_uri": "/salesRep/34",
  "line_items_uri": "/order/123/lineItems"
}
获取有关销售代表的更多信息:

GET /salesRep/34
{
  "sales_rep_name": "Jane Doe",
  "open_orders_uri": "/salesRep/34/orders"
}
获取有关行项目的详细信息:

GET /orders/123/lineItems
{
  "line_items": [
    {"order_uri": "/order/123", "product_uri": "/products/68"},
    {"order_uri": "/order/123", "product_uri": "/products/99"}
  ]
}
等等。我并不是说它是一个完美的API,我只是想证明,思考如何通过RESTful URI以一种很好的规范化、面向资源的方式表达数据模型,这并不完全是火箭科学。但这正是设计问题发挥作用的地方

一方面,我可以很容易地创建一个查询来解决这个问题,但查询的本质要求各种领域概念紧密耦合(换句话说,利用连接将所有规范化数据合并到一个漂亮的、自定义的非规范化结构中)

另一方面,通过思考RESTful API的思维过程,我又回到了将事情很好地划分的道路上——例如,要求“订单123”不应该让我返回这个巨大的图表,在那里我可以看到完整的产品描述、销售代表的电话号码等,一个成熟的HATEOAS级RESTful API的概念要求消费者在需要的时候才进行后续的深入研究


我的问题归结为:用直接查询解决这个用例似乎非常容易,而用一个漂亮整洁的RESTful API解决这个用例似乎非常困难(我正在描绘我组装一周数据所需的1000个个体获取,而查询运行所需的几秒钟)。好的RESTful设计是否有一些优雅的微妙之处,我不明白这会阻止我看到一个好的解决方案,或者我是在尝试将一个圆钉插入一个方孔中(即REST不擅长跨多个资源拉大数据批)?

我将把它作为一个潜在的解决方案扔出去:

从概念上讲,我将此查询的结果视为自身的资源,就像“orderReport”

将其视为自己的资源,API的行为如下:

select o.order_num, o.order_date, p.product_description, sr.sales_rep_name
from order o, line_item li, product p, sales_rep sr
where li.order_num = o.order_num
  and li.product_id = p.product_id
  and sr.sales_rep_id = o.sales_rep_id
  and o.order_date >= [some arbitrary date]
GET /orderReport/[some arbitrary date]
然后,您可以发回一个创建的
201
(如果查询运行相对较快),其中包含一个位置头,如
location:/orderReport/[GUID]
。或者,如果查询需要一段时间才能运行(我真的不知道它是否在我的头顶上),您可以发回一个
202 Accepted
,位置标题为
location:/orderReport/[GUID]/status

然后,您可以对这些URL执行后续get,以获取报告状态(
200 OK
如果仍在处理中没有错误,
201已创建
,如果位置标头指向报告URL,则指向报告本身)

除了满足用例要求所需的严格数据外,报告数据不能包含HATEOAS,如:

{
  [
    {
       "order_num": 123,
       "order_uri": "/orders/123",
       "order_date": 2014-07-03, 
       "product_description": "widget", 
       "sales_rep_name": "Jane Doe",
       "sales_rep_uri": "/salesRep/34"
    },
    {
       "order_num": 456,
       "order_uri": "/orders/456",
       "order_date": 2014-07-04, 
       "product_description": "gadget", 
       "sales_rep_name": "Frank Smith",
       "sales_rep_uri": "/salesRep/53"
    }
  ]
}

这也是我处理这个问题的方式。请注意,GET永远不应该返回201或202,因为这些代码表示正在创建资源。使用POST创建报告,使用GET检索状态/报告对象。你可以在帖子中发送一个有条件的标题,以确保你不会重复。好的建议,谢谢你-我会在我的初步设计中考虑到这一点。