【问题标题】:JSON Response Model design issueJSON响应模型设计问题
【发布时间】:2014-03-05 08:02:31
【问题描述】:

我在设计 API 的 JSON 响应模型方面需要帮助。

假设getUsers 调用返回以下响应模型:

{
    "users":
    [
        {
            "userId": "1",
            "name": "Joe Soap",
            "companyId": "3"
        },
        {
            "userId": "2",
            "name": "Bob Jones",
            "companyId": "3"
        },
        {
            "userId": "3",
            "name": "Mary Jane",
            "companyId": "4"
        },
    ]
}

现在我们有另一个电话,比如说getUsersWithCompany。这包括公司信息。我的问题是:

我应该将这个额外的company 数据包含到每个user 中还是应该是响应模型中的全新companies 列表?

方案一:结合用户和公司

{
    "usersWithCompany":
    [
         {
            "userId": "1",
            "name": "Joe Soap",
            "company":
            {
                "companyId": "3",
                "companyName": "Some Company Ltd",
                "contactNumber": "123123123"
            },
        },
         {
            "userId": "2",
            "name": "Mary Jane",
            "company":
            {
                "companyId": "4",
                "companyName": "Another Company Ltd",
                "contactNumber": "463463383"
            },
        }
    ]
}

优势:当模型被使用时,这可能会更容易,因为在遍历用户列表时公司数据可用。

解决方案 2:用户和公司的单独列表

{
    "users":
    [
        {
            "userId": "1",
            "name": "Joe Soap",
            "companyId": "3"
        },
        {
            "userId": "2",
            "name": "Bob Jones",
            "companyId": "3"
        },
        {
            "userId": "3",
            "name": "Mary Jane",
            "companyId": "4"
        },
    ],
    "companies":
    [
        {
            "companyId": "3",
            "companyName": "Some Company Ltd",
            "contactNumber": "123123123"
        },
        {
            "companyId": "4",
            "companyName": "Another Company Ltd",
            "contactNumber": "463463383"
        },
    ],
}

优势:这可确保用户和公司项目在调用过程中始终保持一致。消费者的品种较少。

我们正在使用 .NET Web Api 2。

【问题讨论】:

  • 这与 ASP 和 .NET 有什么关系?
  • 如果您指的是asp.net-web-api 标签,那是因为我们正在使用该技术。
  • 但是您不会在问题的任何地方使用这些技术。事实上,您的问题与每个 RESTful 系统有关。

标签: json web-services rest asp.net-web-api json.net


【解决方案1】:

设计应该考虑实体是弱还是强。 (弱意味着如果主要实体被删除,它们就不会存活)。所以对于用户和公司,我会有一个单独的 RESTFULL 服务,比如

/users
[
        {
            "userId": "1",
            "name": "Joe Soap",
            "companyId": "3"
        },
        {
            "userId": "2",
            "name": "Bob Jones",
            "companyId": "3"
        },
        {
            "userId": "3",
            "name": "Mary Jane",
            "companyId": "4"
        },
]

/companies
[
        {
            "companyId": "3",
            "companyName": "Some Company Ltd",
            "contactNumber": "123123123"
        },
        {
            "companyId": "4",
            "companyName": "Another Company Ltd",
            "contactNumber": "463463383"
        },
]

除了用户之外,您还需要考虑是否对公司目录有其他参考。如果是这样的话,我什至会相信这种方法。

【讨论】:

    【解决方案2】:

    我会从合并的公司和用户对象的单一列表开始(在双重列表中承诺 YAGNI “你不会需要它”)。这是我见过的最广泛使用的解决方案 - 它允许您稍后轻松添加更多与用户相关的信息。

    稍后,如果发现有效负载大小成为问题(可能是因为您在一个响应中的用户过多),您可以添加一个新方法/服务来返回结果的优化版本。但请记住,压缩输出可以让您获得 long 的方式。有效载荷大小 - 免费!

    但是“优化版本”可以是不同的东西:您建议了一个解决方案(两个列表),但也许您后来发现更好的优化是只返回某种“增量”或“增量”结果 wrt。之前的回应。

    这一切都取决于您的用例 - 过早的优化会让您在可能永远不需要的情况下付出努力。

    【讨论】:

      【解决方案3】:

      我建议将用户和公司视为不同的资源。您可以使用查询参数来指定是否在用户负载中包含公司信息。

      GET /users
      
      {
          "users":
          [
              {
                  "userId": "1",
                  "name": "Joe Soap",
                  "company":
                  {
                      "self": "http:/my.server/companies/3"
                  }
              },
              ...
          ]
      }
      
      GET /users?expand=company
      
      {
          "users":
          [
              {
                  "userId": "1",
                  "name": "Joe Soap",
                  "company":
                  {
                      "self": "http:/my.server/companies/3",
                      "companyId": "3",
                      "companyName": "Some Company Ltd",
                      "contactNumber": "123123123"
                  }
              },
              ...
          ]
      }
      

      这使您可以仅在需要时获取用户信息,在需要时捆绑公司信息,并跟踪从特定用户到其公司的 URI,而无需在客户端上构建 URI。通过将公司作为一个单独的资源,您还可以为自己提供与公司合作的空间,而无需用户作为参考点。

      【讨论】:

        【解决方案4】:

        我看到公司如何连接用户,我写了一个并且我选择了这个方法来解决这个问题

        /user 返回用户列表 /user?connections=OUT 返回所有连接都是用户模型的输出。

        我认为您可以仅通过一条路由发送此信息,例如

        /user 返回用户列表

        {
            "users":
            [
                {
                    "userId": "1",
                    "name": "Joe Soap",
                    "companyId": "3"
                },
                {
                    "userId": "2",
                    "name": "Bob Jones",
                    "companyId": "3"
                },
                {
                    "userId": "3",
                    "name": "Mary Jane",
                    "companyId": "4"
                },
            ]
        }
        

        /user?company=true 返回

        {
            "users":
            [
                {
                    "userId": "1",
                    "name": "Joe Soap",
                    "company": {
                        "companyId": "3",
                        "companyName": "Some Company Ltd",
                        "contactNumber": "123123123"
                    },
                },
                {
                    "userId": "2",
                    "name": "Bob Jones",
                    "company": {
                        "companyId": "3",
                        "companyName": "Some Company Ltd",
                        "contactNumber": "123123123"
                    },
                },
                {
                    "userId": "3",
                    "name": "Mary Jane",
                    "company" : {
                        "companyId": "4",
                        "companyName": "Another Company Ltd",
                        "contactNumber": "463463383"
                    },
                },
            ]
        }
        

        但如果我可以在您的解决方案之间进行选择,我更喜欢第一个

        【讨论】:

          【解决方案5】:

          我确实认为您需要包括这些用户所属的公司。但这里有几个关键点:

          • 如果您返回完整的公司对象,使用您服务的人需要检查 如果公司信息在每次通话中都发生了变化,那会给您的用户增加更多的复杂性。我会只返回公司 ID 并使用相邻的服务来获取这些公司。

          • 我强烈建议使用另一个字段来跟踪此用户信息何时更改,例如时间戳或哈希。这将使您的服务更易于使用,因为人们不需要遍历数组来检查每个用户信息是否在每次调用时都发生了变化。这适用于您提供的每项服务。

          请记住,对于大量数据,返回一小部分数据将获得性能优势。

          【讨论】:

            【解决方案6】:

            我会建议第二种解决方案,因为

            1. 这可确保用户和公司项目始终保持一致 跨电话(如您所述)。

            2. 无需为不同的 API(如 getUsers、getUsersWithCompany)维护单独的模型

            3. 不会有冗余信息。 (这将节省网络带宽)

            4. 如果您将公司和用户绑定到 UI 模型,很容易从基于 Id 的公司模型中获取详细信息。

            5. 如果公司详细信息有任何变化,无需刷新/更新用户模型。

            【讨论】:

              【解决方案7】:

              解决方案 1 是一致的:告诉您用户及其公司。

              解决方案 2 不是。给你用户,顺便说一下,公司。同时做两件事。

              您需要用户及其公司信息吗?然后为他们提供用户,并且根据每个人,公司就是请求者想要的。

              如果您将用户和公司分开,请求者将不得不链接它们。 这是不必要的,就好像请求者想要的公司会请求他们一样。请求者想要用户和他们的公司!

              【讨论】:

                【解决方案8】:

                如果您正在为 Web 设计界面,则应始终牢记响应时间。 除了学术架构问题,这将是您将面临的主要挑战之一。

                问问自己:此调用可能会返回多少个 User 和 Company 实体元组。请记住,随着时间的推移,结果会变得非常大。

                您将尝试将这些界面的信息密度(=熵)保持在非常高的水平。 这意味着,您会尝试消除冗余信息。

                您的解决方案二实现了这些目标。它还有另一个好处: 您的界面使用者更容易解组他的响应。 所有公司实体(希望)都是唯一的,与解决方案一中所需的检查相比,没有必要验证返回的公司实体的完整性和相似性。 与其他类型的操作相比,编组和解组 JSON 与每个线性字符串操作一样非常耗时。字符串越短,结果返回给消费者的速度就越快。

                旁注: 如果您使用解决方案一并通过压缩的 http 连接发送此响应, 与解决方案二相比,http 通道的加载时间不会慢很多。 GZIP 协议可以很好地消除您在公司实体中创建的这些字符串冗余。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-04-23
                  • 2010-11-23
                  • 2013-06-06
                  • 1970-01-01
                  • 2018-02-17
                  相关资源
                  最近更新 更多