【发布时间】:2013-06-06 16:02:09
【问题描述】:
我正在处理一个 ASP.NET WebApi 项目。老板希望返回支持“部分响应”,这意味着尽管数据模型可能包含 50 个字段,但客户端应该能够为响应请求特定字段。原因是,如果他们正在实现例如一个列表,他们根本不需要所有 50 个字段的开销,他们可能只需要 First Name、Last Name 和 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;
}
FieldListFilter.cs
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
// We need to determine if there is a FieldList property of the model that is being used.
// First get a reference to the model.
var modelObject = actionContext.ActionArguments.FirstOrDefault().Value;
string fieldList = string.Empty;
try
{
// Using reflection, attempt to get the value of the FieldList property
var fieldListTemp = modelObject.GetType().GetProperty("FieldList").GetValue(modelObject);
// If it is null then use an empty string
if (fieldListTemp != null)
{
fieldList = fieldListTemp.ToString();
}
}
catch (Exception)
{
fieldList = string.Empty;
}
// Update the global ContractResolver with the fieldList value but for efficiency only do it if they are not the same as the current ContractResolver.
if (((DynamicContractResolver)GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver).FieldList != fieldList)
{
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new DynamicContractResolver(fieldList);
}
}
然后我可以发送一个带有 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 中实现了这一点,他们称之为“部分响应”,效果很好。我的实现在某种程度上有效,但我担心它会因多个同时请求而中断。
建议?提示?
【问题讨论】:
-
仅供参考...检查
$select对 Json 格式化程序的功能支持,该功能将在下一版本中推出:aspnetwebstack.codeplex.com/… -
别以为我有时间等待下一个版本。我们在不到 2 个月的时间里就有了一个演示,我有很多东西要实现。更重要的是,是我的方法从根本上坏掉了。我是根据网络上的建议和教程实现的,但我只是对它的基础有一些我不完全理解的问题。
-
如果您在每个请求上创建一个
DynamicContractResolver的新实例并将其用作ContractResolver,则不应该存在并发问题。 -
muratgu,我想你回答了我的问题,谢谢!和 Kiran - 感谢我将其归档以备后用的信息。我会给这个一天,看看有没有其他人有建议或 cmet,然后我会关闭它。
标签: json model-view-controller asp.net-web-api