【问题标题】:How to do partial responses using ASP.Net Web Api 2如何使用 ASP.Net Web Api 2 进行部分响应
【发布时间】:2013-12-02 06:38:02
【问题描述】:

我对 API 设计和 MVC 概念真的很陌生,但据我所知,像 GET /api/products 应该返回产品列表,而 GET /api/products/1 应该返回单个产品。在速度方面,我的感觉是 /api/products 应该返回更少的信息,即只返回 id 和 name,而 /api/products/1 应该返回更多,即 id、name 和 description。

据我所知,处理此问题的最佳方法是在 /api/products 端点中不返回产品类的某些字段。这在 /api/products?fields=name 的情况下尤其必要。我正在使用 ASP.Net Web Api 2 并尝试了以下方法:

有什么简单的方法可以做我想做的事吗?

要不然你能推荐一个比我现在做的更好的 API 设计吗?

【问题讨论】:

  • 也许您可以在 /api/products 返回一个匿名类型,然后返回您的详细产品类型 /api/products/10。
  • 感谢@jtlowe - /api/products 和 /api/products/10 都通过实体框架检索 Product 对象,因此我们在这两种情况下都拥有完整的 Product 对象。当然,既然我们已经在 /api/products 上拥有了完整的 Product 对象,那么肯定有某种方法可以只返回 List 但省略了一些属性?
  • 取决于数据库的大小/设置,可能还有序列化程序。听起来您只想加载 /api/products 所需的信息,而不是完整的 Product。如果您从 Products 表中检索所有完整记录,则可能会影响性能,这听起来像是您试图避免的。马丁的回答是一个很好的方法。另请查看支持 OData 查询。这样可以更轻松地进行过滤等。
  • 主要的性能问题是在响应中将大量数据传递给客户端,而不是数据库拉取。在这方面,Martin 的方法听起来不错,但我看不出它是可扩展的。例如,如果我们要引入 /api/stores/1/products 来返回 Product 对象但这次只公开 ID、名称和 SKU 编号怎么办?我们现在需要创建另一种类型的 Product 类吗?
  • 在您刚刚概述的情况下,您可以对 /api/products 和 /api/store/1/products 使用相同的初始 Product 对象。对于 ProductDetail 视图 - 您可以对 /api/products/10 进行 api 调用,而不是继续向下 /api/store/1/products/10。

标签: c# asp.net-mvc json asp.net-web-api


【解决方案1】:

您也可以使用 WebApi.PartialResponse (http://www.nuget.org/packages/WebApi.PartialResponse/)。这是我编写的一个包,它使用 LINQ to JSON (Json.NET) 来操作返回的对象。它使用 Google 在其 API 中使用的字段语法,例如:

  • fields=items/id,playlistItems/sn-p/title,playlistItems/sn-p/position
  • fields=items(id,sn-p/title,sn-p/position)
  • fields=items(id,sn-p(title,position))

您可以在 GitHub 项目页面上找到更多信息:https://github.com/dotarj/PartialResponse

【讨论】:

  • 有没有办法将它与集合一起使用?假设我从 web api 返回一个分页集合,而不是获取分页集合的全部内容,我只想要 id,并命名集合中的每个项目。我可以让一切正常工作,除了上面的场景,这是我真正想用它的:)
  • 我阅读了关于部分响应的 Google 开发文档,认为可能会有一些见解。看起来这应该适用于集合。 developers.google.com/drive/web/performance 我的 JSON 有类型信息,会不会把事情搞砸了? : { $id: "4", $type: "System.Collections.Generic.List`1[[Acme.Models.FluidModel, KNE.Athena.Models]], mscorlib", $values: [ { $id: " 5", $type: "Acme.Fluids.FluidModel, Acme.Models", 链接: {}, id: 1, name: "Fluid", },
【解决方案2】:

我建议在返回实体列表时使用单独的类来映射。

尤其是问题不仅在于您返回给用户的内容,还在于您从数据库中选择的内容。

因此,让获取和实体返回 Product 对象,并获取实体列表返回 ProductLink 对象或类似的东西。

编辑

根据 jtlowe 的评论,如果您有许多不同的方法返回产品属性的细微变化,请使用匿名类(尽管我会质疑这是否一定是一个好的设计)。

在你的行动中考虑这样的事情

return from p in this.context.Products
       select new
       {
           p.Id,
           p.Name,
           p.SKU
       };

这个:

  • 仅从数据库中选择您需要的列。
  • 不需要为产品的新变体定义额外的类

这并不容易将该语句的结果传递给其他方法,因为您只能将它作为 IEnumerable、对象或动态返回。如果您将它放在控制器中,那么它可能就足够了。如果您正在实现存储库模式,那么如果您使用匿名类型,您将无法返回强类型列表。

【讨论】:

  • 感谢 Martin 的建议 - 我想我们会使用这种方法。您建议如何处理 ?fields=id,name 问题?
  • 其实想一想,请看我上面对jtlowe的评论。这种方法肯定是不可扩展的吗?
  • 如果您的 Product 对象不是包含所有产品详细信息的实际 Product 对象,这将扩展。例如:Product {ID, SKU, Name}, ProductDetails {ID, SKU, NAME, Manufacturer, Contact, Price, Origin, Height, Width, ..., ..., ..., ..., .. .}.
  • 感谢您的帮助 jtlowe 和 Martin Booth - 这真的帮助我理顺了思路!不过,关于部分响应,我找不到任何关于如何处理 /api/products?fields=color,size,name 之类的具体内容。 .NET 生态系统中是否有任何最佳实践?如果可能,我宁愿不使用 OData。
【解决方案3】:

对这个话题感到困惑,只是想分享我的感受 - 也许它可以帮助其他人:) 我建议使用 OData 之类的东西。 你可以实现它,这样你就可以写/api/products?$select=Id,Name,Price

一些优点:

  • 通过 OData,您可以使用更多功能,如 $filter、$orderby 来处理过滤器并对其进行排序
  • $skip, $top, $count 以获得良好的分页
  • 更多 $-functions :)
  • 您可以直接将其应用于IQueryable<T>。为什么这么棒?您不仅可以减少 API 的响应结果,还可以减少数据库生成的结果,从而使您的应用程序更快。 - 您甚至不必更改查询

一些缺点:

  • 您不能直接过滤计算的列
  • 设置它需要一点时间

提示:有时最好在参数中使用ODataQueryOptions<T> 而不是完整的实现。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-16
    • 1970-01-01
    • 2020-05-03
    • 2017-12-09
    • 2015-04-01
    • 2015-01-12
    • 2019-11-25
    相关资源
    最近更新 更多