在Spring(“REST”)控制器中处理关系

在Spring(“REST”)控制器中处理关系,spring,rest,spring-mvc,spring-boot,spring-rest,Spring,Rest,Spring Mvc,Spring Boot,Spring Rest,我目前正在开发一个具有多个模型对象的应用程序,这些模型对象之间有很多关系 例如,我有一个用户对象和一个组对象,其中有多个用户 我们为这些对象公开的端点是: /users /users/{uid} /users/{uid}/groups /users/{uid}/groups/{gid} /groups /groups/{gid} /groups/{gid}/users /groups/{gid}/users/{uid} 到目前为止,这很容易。现在让我们假设每个用户和每个组都可以有设置、消息、

我目前正在开发一个具有多个模型对象的应用程序,这些模型对象之间有很多关系

例如,我有一个用户对象和一个组对象,其中有多个用户

我们为这些对象公开的端点是:

/users
/users/{uid}
/users/{uid}/groups
/users/{uid}/groups/{gid}

/groups
/groups/{gid}
/groups/{gid}/users
/groups/{gid}/users/{uid}
到目前为止,这很容易。现在让我们假设每个用户和每个组都可以有设置、消息、化身等等

现在我们可以公开端点,例如

/users/{uid}/avatar
/groups/{uid}/avatar
/users/{uid}/messages
/users/{uid}/messages/{mid}
/groups/{gid}/messages
/groups/{gid}/messages/{mid}
/users/{uid}/settings
/groups/{gid}/settings

即使只有6个关系(用户->化身,组->化身,用户->消息,组->消息,用户->设置,组->设置),当我们考虑各种CRUD操作(放置,删除,POST,GET,…)/P>时,请求映射的数量(端点)都会大大增加。 如果我添加另一个资源,例如可以包含多个组的groupOfGroups,那么我将公开的RequestMappings的数量将成倍增加

/gog/{gogid}/groups
/gog/{gogid}/groups/{gid}
/gog/{gogid}/groups/{gid}/users
/gog/{gogid}/groups/{gid}/users/{uid}
/gog/{gogid}/settings
/gog/{gogid}/messages
/gog/{gogid}/avatar
我还将为CRUD操作添加RequestMappings

为了避免代码重复,我在每个主控制器中都使用了catch-all请求映射。例如,在UserController中,我将有以下requestMappings

GET /users
GET /users/{id}
POST /users
PUT /users/{id}
DELETE /users/{id}
/**
/**映射将捕获/users/{uid}/messages/{mid}调用。在/**映射对应的方法中,我将执行以下操作

  • 标记化请求URL以获取/users/{uid}和 restofurl(在本例中,/message/{mid},但可以是/settings或/avatar)
  • 检查具有该uid的用户是否存在,如果不存在,则返回404
  • 添加一个requestAttribute,其中包含键用户和值{uid}
  • 转发至restofurl
  • 这将把请求转发给MessageController,在那里我可以使用一些QueryDSL构建器来构建QueryDSL,以便只获取请求属性中任何内容的消息(在本例中,它是具有{uid}的用户,但它可以是具有id{gid}的组或具有id{gogid}的组的组)

    我想知道这是否是正确的解决方案,或者是否有更好的方法处理关系,而无需转发,也无需重复代码

    至少在我看来,有三个端点是没有意义的

    /users/{uid}/messages
    /users/{uid}/settings
    /users/{uid}/avatar
    
    检查用户是否存在,这将对groups和groupOfGroups执行相同的功能,但唯一的区别是我们正在检查该组是否存在或groupOfGroups是否存在

    需要注意的是,像Swagger这样的工具当然不支持这种方法,甚至向前看,也不能使用@RestController,只能使用更通用的web mvc注释@Controller


    我想知道这里最好的方法是什么。

    在我看来,您试图在URI路径中表示关系(或者也可能是导航)。REST中的关系表示为链接,来自:

    可以说一个团队有一个文档资源(/team/{id}/players),它是指向团队中的玩家(/player/{id})的链接列表,玩家可以有一个文档资源(/player/{id}/teams),它是指向该玩家所属团队的链接列表

    REST与URI的外观无关,更多的是向客户端发送指向特定状态下可用操作的链接(例如:如果我在
    /users/1
    中,我可以转到
    /users/1/messages
    )。只要服务器构建链接,客户端只使用它们,链接的结构就无关紧要

    但是,您的一些URI包含了他们需要的更多信息(并且可能需要URI构建(而不是rest)客户端了解它不需要知道的事情):
    /gog/{gogid}/groups/{gid}/users/{uid}
    /groups/{gid}/users/{uid}
    /users/{uid}
    因为用户的数据总是可以由
    {uid}
    检索

    另一种可能性是
    /groups/{gid}/users/{uid}
    实际上意味着“表示用户和组之间关联的资源”例如,包括用户加入组的日期;如果是这种情况,那么对我来说这是一个新资源,例如:
    成员身份
    ,具有自己的ID,可以映射到
    /membership/{membership ID}
    ,并且
    /groups/{gid}/users/{uid}
    可以重定向到它。成员身份将在
    /groups/{gid}中列出/会员资格


    /users/{uid}/avatar
    /groups/{gid}/avatar
    可以重定向到他们的
    /avatar/{avatar id}
    。如果共享了avatar,那么在使用HTTP缓存时这可能会有好处。

    在我看来,你是在试图表示关系(或者可能也是导航)在URI路径中。REST中的关系表示为链接,来自:

    可以说一个团队有一个文档资源(/team/{id}/players),它是指向团队中的玩家(/player/{id})的链接列表,玩家可以有一个文档资源(/player/{id}/teams),它是指向该玩家所属团队的链接列表

    REST与URI的外观无关,更多的是向客户端发送指向特定状态下可用操作的链接(例如:如果我在
    /users/1
    中,我可以转到
    /users/1/messages
    )。只要服务器构建链接,客户端只使用它们,链接的结构就无关紧要

    但是,您的一些URI包含了他们需要的更多信息(并且可能需要URI构建(而不是rest)客户端了解它不需要知道的事情):
    /gog/{gogid}/groups/{gid}/users/{uid}
    /groups/{gid}/users/{uid}
    /users/{uid}
    因为用户的数据总是可以由
    {uid}
    检索

    另一种可能性是,
    /groups/{gid}/users/{uid}
    实际上意味着“一个资源,表示一个用户之间的关联