Json ASP.NET WebApi和部分响应

Json ASP.NET WebApi和部分响应,json,model-view-controller,asp.net-web-api,Json,Model View Controller,Asp.net Web Api,我有一个ASP.NET WebApi项目,我正在工作。boss希望返回支持“部分响应”,这意味着尽管数据模型可能包含50个字段,但客户机应该能够为响应请求特定字段。原因是,如果他们正在实现一个列表,他们根本不需要所有50个字段的开销,他们可能只需要名字、姓氏和Id来生成列表。到目前为止,我已经实现了一个解决方案,通过使用自定义契约解析器(DynamicContractResolver),当请求传入时,我通过OnActionExecuting方法中的过滤器(FieldListFilter)窥视它,

我有一个ASP.NET WebApi项目,我正在工作。boss希望返回支持“部分响应”,这意味着尽管数据模型可能包含50个字段,但客户机应该能够为响应请求特定字段。原因是,如果他们正在实现一个列表,他们根本不需要所有50个字段的开销,他们可能只需要名字、姓氏和Id来生成列表。到目前为止,我已经实现了一个解决方案,通过使用自定义契约解析器(DynamicContractResolver),当请求传入时,我通过OnActionExecuting方法中的过滤器(FieldListFilter)窥视它,并确定名为“FieldList”的字段存在,如果存在,我将使用DynamicContractResolver的新实例替换当前ContractResolver,并将字段列表传递给构造函数

一些示例代码

DynamicContractResolver.cs

protected override IList<JsonProperty> CreateProperties(Type type, Newtonsoft.Json.MemberSerialization memberSerialization)
    {
        List<String> fieldList = ConvertFieldStringToList();

        IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
        if (fieldList.Count == 0)
        {
            return properties;
        }
        // If we have fields, check that FieldList is one of them.
        if (!fieldList.Contains("FieldList"))
            // If not then add it, FieldList must ALWAYS be a part of any non null field list.
            fieldList.Add("FieldList");
        if (!fieldList.Contains("Data"))
            fieldList.Add("Data");
        if (!fieldList.Contains("FilterText"))
            fieldList.Add("FilterText");
        if (!fieldList.Contains("PageNumber"))
            fieldList.Add("PageNumber");
        if (!fieldList.Contains("RecordsReturned"))
            fieldList.Add("RecordsReturned");
        if (!fieldList.Contains("RecordsFound"))
            fieldList.Add("RecordsFound");
        for (int ctr = properties.Count-1; ctr >= 0; ctr--)
        {
            foreach (string field in fieldList)
            {
                if (field.Trim() == properties[ctr].PropertyName)
                {
                    goto Found;
                }
            }
            System.Diagnostics.Debug.WriteLine("Remove Property at Index " + ctr + " Named: " + properties[ctr].PropertyName);
            properties.RemoveAt(ctr);
        // Exit point for the inner foreach.  Nothing to do here.
        Found: { }
        }
        return properties;
    }
然后,我可以发送一个请求,其中json内容负载如下所示:

{
  "FieldList":"NameFirst,NameLast,Id",
  "Data":[
    {
      "Id":1234
    },
    {
      "Id":1235
    }
  ]
}
我会收到这样的回复:

{
  "FieldList":"NameFirst,NameLast,Id",
  "Data":[
    {
      "NameFirst":"Brian",
      "NameLast":"Mueller",
      "Id":1234
    },
    {
      "NameFirst":"Brian",
      "NameLast":"Mueller",
      "Id":1235
    }
  ]
}
我相信使用ContractResolver可能会遇到线程问题。如果我为一个请求更改它,那么它是否对以后的所有请求都有效,直到有人在另一个请求上更改它(通过测试似乎是这样),如果是这样的话,那么我看不出它对我的目的有用

总之,我正在寻找一种拥有动态数据模型的方法,以便客户机可以逐个请求地配置请求的输出。谷歌在他们的web api中实现了这一点,他们称之为“部分响应”,效果非常好。我的实现在某种程度上是可行的,但我担心它会因多个同时请求而中断


建议?提示?

您不能触摸配置。您需要基于每个请求的合同解析程序。你可以像这样在你的动作方法中使用它

public class MyController : ApiController
{
    public HttpResponseMessage Get()
    {
        var formatter = new JsonMediaTypeFormatter();
        formatter.SerializerSettings.ContractResolver = 
              new DynamicContractResolver(new List<string>()
                       {"Id", "LastName"}); // you will get this from your filter

        var dto = new MyDto()
              { FirstName = "Captain", LastName = "Cool", Id = 8 };

        return new HttpResponseMessage()
        {
            Content = new ObjectContent<MyDto>(dto, formatter)
        };
        // What goes out is {"LastName":"Cool","Id":8}
    }
}
公共类MyController:ApiController
{
公共HttpResponseMessage Get()
{
var formatter=新的JsonMediaTypeFormatter();
formatter.SerializerSettings.ContractResolver=
新的DynamicContractResolver(新列表()
{“Id”,“LastName”});//您将从筛选器中获取此信息
var dto=新的MyDto()
{FirstName=“船长”,LastName=“酷”,Id=8};
返回新的HttpResponseMessage()
{
内容=新对象内容(dto、格式化程序)
};
//出去的是{“LastName”:“Cool”,“Id”:8}
}
}

通过这样做,您将自己锁定为响应消息的JSON内容类型,但您已经通过使用JSON.NET特定功能做出了决定。另外,请注意,您正在创建一个新的JsonMediaTypeFormatter。因此,您在配置中配置的任何内容(如媒体类型映射)都不适用于这种方法。

这是一种更简单的解决方案

创建一个模型类,该类包含所有50个可为null类型的成员。 为请求的成员分配值。 只需以正常方式返回结果

在WebApiConfig.Register()中,必须设置空值处理

   config.Formatters.JsonFormatter.SerializerSettings =
        new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };

我知道这个问题是很多年前提出的,但如果您希望在现代版本的框架中实现这一点,我建议现在使用OData服务()

仅供参考…检查下一版本即将推出的Json格式化程序的
$select
功能支持:我想我没有时间等待下一版本。我们在不到两个月的时间里就有了一个演示,我有很多东西要实现。更重要的是,我的方法从根本上被打破了。我是根据Web上的建议和教程实现的,但是我有一些关于它的基础的问题,我不完全理解。如果在每个请求上创建一个新的实例:<代码> DyrimeCurraseRealver <代码>,并使用它作为<代码> CurraseDeasver < /C> >,那么就不应该存在并发发行。我想你回答了我的问题,谢谢!还有Kiran-谢谢你提供的信息,我已经把它归档,以备日后使用。我会给这一天时间,看看是否有其他人有建议或意见,然后我会关闭它。Badri,通过触摸全局配置,我并不是将自己锁定在Json中。我已经通过发送请求头accept:application/xml来测试了这一点,它返回xml而不考虑contractresolver,因为它在返回路径上被绕过,并且使用xml解析器。我也不想将自己锁定在json中,项目的一个要求是最终用户能够根据请求返回json或xml。但是,如果他们选择xml,老板并不关心部分内容,因此我现在不关心流程的这一方面。我将立即对此进行研究,因为我同意这将简化一些过程,并将逻辑移到API的数据转换层,在那里我也可以有更多的控制权。我实现了此解决方案,并取消了DynamicContractResolver。这似乎工作得很好,并且增加了可读性、易懂性,并为我提供了一些额外的灵活性,比如“必需的返回字段”等等。感谢您的帮助。config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling=NullValueHandling.Ignore;
   config.Formatters.JsonFormatter.SerializerSettings =
        new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };