REST API架构:如何表示连接的表

21

问题

我有一个复杂的查询,连接了三个表,并返回一组行,每行都有来自其兄弟表的数据。如何以RESTful方式表示这种情况?

顺便说一下,我知道并不一定有“正确”的方法,但我对了解针对这种情况可能是最具可扩展性和耐用性的解决方案很感兴趣。

背景

过去,我曾经代表过单个表,这些表或多或少地反映了url的字面结构。例如,url GET /agents/1/policies 会导致类似于select * from policies where agent_id = 1的查询。

假设

看起来url并不一定要与数据库层的结构紧密耦合。例如,如果复杂查询是这样的:

select
  agent.name as agent_name,
  policy.status as policy_status,
  vehicle.year as vehicle_year
from
  policies as policy
  join agents as agent on policy.agent_id = agent.id
  join vehicles as vehicle on vehicle.policy_id = policy.id
where 1=1
  and policy.status = 'active';

# outputs something like:
{ "agent_name": "steve", "policy_status": "single", "vehicle_year": "1999" }

我可以将这个查询表示为一个URL而不是表格作为URL。该URL可以是/vehicles,如果有人想要查询它(使用id或其他参数,如/vehicles?vehicle_color=red),我只需将该值传递到预处理语句中。

奖励问题

  • 这是反模式吗?
  • 我的查询应该始终针对现有表格运行,而不是预处理语句吗?
感谢您的帮助!
1个回答

21

您希望从数据库表和查询中脱离出来,考虑基本资源。在您的例子中,明显这些是agentcustomervehiclepolicy

资源与集合

我在您的例子中看到的一个错误是,您没有使用复数将集合与资源分开,这在处理搜索和逻辑上(对于控制器路由)可能很有用。您的例子中有:

GET /agents/1/policies

假设改为GET /agent/1/policies

现在您可以清楚地区分幂等资源的位置:/agent/1,以及查找/搜索代理商集合:/agents

遵循这样的思路,您开始将枚举关系与API中的每个关系的一侧分离开来,这是本质上多余的。

在您的示例中,很明显,策略并不是由代理专门拥有的。策略应该是一个独立的资源,可通过某个幂等URL进行标识,并使用任何唯一标识该策略的ID来查找该策略,即/policy/{Id}

搜索集合

现在,这样做的好处是,您可以通过/policies来分离查找策略,其中仅返回特定代理商的策略只是访问该集合的许多不同方式之一。

因此,与其使用GET /agents/1/policies,您应该通过GET /policies?agent=1来查找与代理关联的策略。

预期结果是匹配策略的资源标识符的集合:

{ "policies" : ["/policy/234234", "/policy/383282"] }

那么你如何获得最终结果?

对于给定的策略,您期望返回所有相关信息,就像您的查询一样,只是没有限制选择子句。由于您想要的是过滤版本,处理它的方法是包括过滤条件。

GET /policy/234234?filter=agentName,policyStatus,vehicleYear

话虽如此,这种方法有陷阱,我因为很多原因而对它提出质疑。如果您查看原始资源列表,每个资源都可以视为对象。如果您正在客户端构建对象图,则策略的完整信息将包括所有相关资源的资源定位符:

{ ... Policy data + "customer": "/customer/2834", "vehicle": "/vehicle/88328", "agent": "/agent/32" }

客户端的工作是访问代理、车辆和客户的数据,而不是你的工作每次需要查看那些数据时都要重复输出所有数据。

这样更好,因为它既符合RESTful,又支持REST的许多目标,如幂等性、缓存等。

这也更好地允许客户端在本地缓存代理的数据,并确定是否需要获取该数据或只是访问其已经缓存的数据。最坏的情况下可能需要进行3到4个REST调用。

Bonus questions

REST有一些灰色地带。你必须解释Fielding的观点,因此在处理事物时常常有不同的意见。虽然提供像GET /agents/1/policies这样的API以提供与代理关联的政策列表的方法经常被使用,但在我的经验中,当需要细化模型关系到底层资源时,这种方法变得限制性和多余。

至于你关于查询的问题,只要你保持一致,在访问基础数据和组织数据的方式上并没有什么区别。通常情况下(出于性能考虑),API不会返回资源标识符,并开始像我之前所示的那样返回数据。这是一个很滑的坡,你只是把REST API转化为一堆查询的前端。在那个时候,你的API可能就变成了: GET \query?filter=agent.name, policy.status, vehicle.year&from=policies&join=agents,vehicles&where=...


5
很抱歉您的回复已经一年了,但我只是想说谢谢您对此的深思熟虑!我下次一定会重新考虑您对我的建议。 - Mike Fleming
这太棒了 - esraa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接