2

我有一个公开用户的 Web API。

每个用户都有一个经理,他也是一个用户。

在我的 EDMX 中,我在“用户”实体上有一个 self 1..* 导航属性,它是从经理到用户的导航的“协作者”,也是从用户到他的经理的导航的“经理”。

我的 JSON API 使用 NewtonJSON 来序列化实体。

为了自定义 API 调用的 JSON 结果,我在查询字符串中实现了关键字“字段”。使用“字段”可以获取用户的部分 JSON 表示。

调用如下所示:/api/users?fields=id,name,department,picture

你会从一个完整的 C# 用户对象中得到一个只有 id、name、department 和 picture 属性的 JSON。

我已经使用 IContractResolver 的自定义实现来实现这一点。

问题是 NewtonJSON 合约解析器“按类型”而不是“按对象”工作,这意味着您可以告诉序列化程序序列化声明类型的此类成员而不是另一个成员,但您不能告诉它(如据我所知,这就是我在这里问的原因)序列化这种类型的该对象的此类成员,而不是同一类型的另一个对象上的相同成员。

也就是说,我的问题是当我要求:/api/users?fields=id,name,manager

我在每个用户对象上通过管理器成员的递归序列化得到响应,如下所示:

[{
    id: 123,
    name: "Foo",
    manager:
    {
        id: 124,
        name: "Foo",
        manager:
        {
            id: 125,
            name: "Foo",
            manager:
            {
            ...
            }
        }
    }
},
{
    id: 124,
    name: "Foo",
    manager:
    {
        id: 125,
        name: "Foo",
        manager:
        {
        ...
        }
    }
},
{
    id: 125,
    name: "Foo",
    manager:
    {
    ...
    }
}]

我还实现了请求部分子实体响应的能力,如下所示:

/api/users?fields=id,name,manager.id

但是由于主对象(这里是用户)和子对象(经理)都属于同一类型,因此它不起作用。

是否有人已经使用 NewtonJSON 来实现部分响应 Web API?您如何设法使用嵌入式自我类型实体?

谢谢你的建议。

4

1 回答 1

0

您可以编写一个动作过滤器来理解字段参数并转换响应。我有一个将响应对象转换为字典的示例实现。键是客户端询问的字段,值是响应中返回的对象的字段值。该实现是一个示例,还远未完成:)

public class FieldFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        var fieldsParameter = actionExecutedContext.Request.GetQueryNameValuePairs().Where(kvp => kvp.Key == "fields");
        if (fieldsParameter.Count() == 1)
        {
            object response;
            object newResponse;

            if (actionExecutedContext.Response.TryGetContentValue(out response))
            {
                string[] fields = fieldsParameter.First().Value.Split(',');

                IEnumerable<object> collection = response as IEnumerable<object>;
                if (collection != null)
                {
                    newResponse = collection.Select(o => SelectFields(fields, o));
                }
                else
                {
                    newResponse = SelectFields(fields, response);
                }

                actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(HttpStatusCode.OK, newResponse);
            }
        }
    }

    private static Dictionary<string, object> SelectFields(string[] fields, object value)
    {
        Dictionary<string, object> result = new Dictionary<string, object>();

        foreach (string field in fields)
        {
            result.Add(field, GetValue(field, value));
        }

        return result;
    }

    private static object GetValue(string indexer, object value)
    {
        string[] indexers = indexer.Split('.');

        foreach (string property in indexers)
        {
            PropertyInfo propertyInfo = value.GetType().GetProperty(property);
            if (propertyInfo == null)
            {
                throw new Exception(String.Format("property '{0}' not found on type '{1}'", property, value.GetType()));
            }

            value = propertyInfo.GetValue(value);

            if (value == null)
            {
                return null;
            }
        }

        return value;
    }
}
于 2013-02-08T19:36:30.033 回答