在 RESTful Web 服务中按 ID 和用户名查找
Posted
技术标签:
【中文标题】在 RESTful Web 服务中按 ID 和用户名查找【英文标题】:FInd by ID and Username in RESTful Web Services 【发布时间】:2020-01-25 13:27:04 【问题描述】:我对单例资源的最佳偏好感到困惑。我希望端点的用户 ID 为:
/users/id
但我也想要username
作为参数:
/users/username
我正在使用 Spring MVC(通过 Spring Boot),它说 2 个端点存在冲突。所以我决定首先为用户检索单例资源。但是为了让客户端仍然使用用户名作为参数,我添加了用户名作为用户集合资源的查询参数:
/users?username=<username>
在我的repository
和service
层中,返回是Optional<User>
,即它是empty
或one user
结果。但是在controller
中,我将其包装在list
中,可以为空也可以为1,以使其与/users
作为列表的返回一致。
这样的设计合理吗?还是有更好的设计?谢谢。
【问题讨论】:
如果两个查询在同一个数据库上,你可以使用'UNION'来加入查询, 【参考方案1】:基本上,您有两种选择:
您要么有两个单独的端点,例如,/users/byId/id
和 /users/byName/username
,每个端点都映射到不同的控制器方法
class UserController
@GetMapping("/byId/id")
Optional<User> findById(@PathVariable("id") String id)
...
@GetMapping("/byName/username")
Optional<User> findByUsername(@PathVariable("username") String username)
...
或者一个端点/users/id-or-name
,然后你进行联合查询,所以:findAllByIdOrName()
我倾向于说在单独的端点中执行它更清洁、更高效。
编辑
RESTful 约定意味着您有 2 个端点:
@GetMapping("/id")
Optional<User> findById(@PathVariable("id") String id)
...
@GetMapping("/")
List<User> findUsers(@RequestParam("username") String username)
// if username is not empty, filter users
// we could also filter with other user properties according to specs
...
除此之外的任何东西都已经偏离了惯例。
【讨论】:
第一个选项不遵循 RESTful URI 约定,所以我不会考虑它。对于第二个,那么我的@PathVariable 是什么? 谢谢。我现在才读。实际上,您的第二个选择是我考虑的那个。那是我在上面发布的确切选项。很高兴,我推动了它。【参考方案2】:我遇到了同样的问题。两个端点发生冲突,所以事件 /user/id 不想工作,所以我确实给 findbyusername 一个不同的端点,例如:
@GetMapping("/user/username/username")
【讨论】:
【参考方案3】:您应该尽量防止客户端使用字符串操作来组装 URI,因为这样客户端必须对服务器内部有深入的了解。
不过,优雅的 URI 方案通常是合理资源结构的标志。如果 URI 设计得很好,那么对于想要使用 REST API 的开发人员来说,这会变得更轻松;客户会喜欢易于记忆的 URI。手动编辑 URI 的能力(例如,通过在“/”之后截断其中的一些)是一个明显的优势。
如您所见:URI 标识资源。资源是“事物”,名词,而不是动作或动词!如果您进行了腐蚀性资源设计,匹配的名称通常会自己出现。但是,我想在这里举一个例子,由于它的结构或名称,它可能表明存在问题。
http://example.com/customers/create?name=XYZ
这里有点混乱: 动词“create”在 URI 中没有位置,并且该结构表明该操作由 URI 标识,而不是由 HTTP 动词标识。
您的 URI 应该包含名词。对于按预期标识资源的 URI,您还可以轻松想象不同的 HTTP 方法是如何工作的:PUT 更新 DELETE 删除,GET 获取信息。以下是上述 URI 看起来更好的示例:
发布http://example.com/customers/
透明和可理解的 URI 设计的一个明显规则是将层次关系映射到 URI 的路径元素结构。
示例: http://example.com/organization/it/support/networks
因此,如果您是服务器的开发人员并因此是 URI 的设计者,您应该确保 http://example.com/organization/it/support 上的 GET 也返回有用的结果。在您的客户中,无论如何您都不应该做出这样的假设! 这从根本上有助于分布式系统的稳定性,也称为robustness principle 或 Postel 定律。
对于过滤器的 URI 设计,它们有不同的可能性。最明显的是使用查询参数。对于 /customers 下您要为来自德国的所有客户过滤的客户列表,示例 URI 可能如下所示:
http://example.com/customers?country=Germany 多个查询组件可以用“&”链接:
http://example.com/customers?country=Germany&year=2020
在这里,他们也通过这样的 URI 设计满足了用户的期望。如果查询字符串被省略 (?country=Germany&year=2020),用户将被带到未过滤的所有客户列表中。
拥有稳定的 URI 很重要,酷炫的 URI 不会改变 (Tim Berners-Lee)。
回答你的问题:
设计不应被高估;它应该是有用的,但并不完美(无论如何这是不可能实现的)。
【讨论】:
【参考方案4】:您不能对一个特定映射使用相同的端点名称。在这里,您对两个 @GetMapping 使用相同的端点名称。如果你想做这件事,你应该使用参数。
class UserController
@GetMapping("/users")
Optional<User> findById(@RequestParam(required = false) String id, @RequestParam(required = false) String username)
if (!username.equals(""))
// Response using a username
if (!id.equals(""))
// Response using an id
【讨论】:
我想要遵循 RESTful URI 约定的更简洁的 URI。所以id
作为查询字符串是不可接受的。以上是关于在 RESTful Web 服务中按 ID 和用户名查找的主要内容,如果未能解决你的问题,请参考以下文章
使用 OAuth2 实现 Spring RESTful Web 服务 - 将 id 令牌保存为 id 会话
如何从 android 调用 RESTful Web 服务?
使用 Jersey 和 Apache Tomcat 构建 RESTful Web 服务