JSON响应模型设计问题

时间:2014-03-05 08:02:31

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

我需要帮助设计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列表?

解决方案1:结合用户和公司

{
    "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.

8 个答案:

答案 0 :(得分:1)

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

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

但是“优化版本”可能是不同的东西:你提出了一个解决方案(两个列表),但也许你后来发现更好的优化只会返回某种“增量”或“增量”结果。先前的回复。

这完全取决于您的使用案例 - 过早的优化会让您花费精力而不需要它。

答案 1 :(得分:1)

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

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。通过将公司作为一个单独的资源,您还可以自己与公司合作,而无需将用户作为参考点。

答案 2 :(得分: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"
        },
]

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

答案 3 :(得分:1)

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

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

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

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

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

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

答案 4 :(得分:1)

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

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

我认为您只能在一条路线中发送此信息,例如

/用户返回用户列表

{
    "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 return

{
    "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 :(得分:1)

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

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

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

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

答案 6 :(得分:0)

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

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

您需要用户及其公司信息吗?然后给予用户以及每个公司,这是请求者想要的。

如果您将用户和公司分开,请求者必须链接它们。 这是不必要的,好像请求者希望公司会要求他们。请求者希望用户及其公司!

答案 7 :(得分:0)

如果您正在为网络设计界面,则应始终牢记响应时间。 除了学术性的建筑事务外,这将是您将面临的主要挑战之一。

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

您将尝试将这些接口的信息密度(=熵)保持得非常高。 这意味着,您尝试消除多余的信息。

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

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