문제

I have a Web API that exposes users.

Each user has a manager, who is also a user.

In my EDMX, I have a self 1..* Navigation Property on the "user" entity, which is "collaborators" for the navigation from manager to users and which is "manager" for the navigation from the user to his manager.

My JSON API uses NewtonJSON to serialize entities.

In order to customize the JSON result of API calls, I have implemented the keyword "fields" in the querystring. Using "fields" enable the possibility of getting a partial JSON representation of a user.

A call looks like this : /api/users?fields=id,name,department,picture

And you get a JSON with only id, name, department and picture properties out of a complete C# user object.

I have achieve this using a custom implementation of IContractResolver.

The problem is that the NewtonJSON contract resolver works "by Type" and not "by object", which means that you can tell the serializer to serialize such member of a declaring type and not another one, but you can't tell it (as far as I know, that's why I ask here) to serialize such member of this object of such Type and not the same member on another object of the same type.

That said, my problem is when I ask for : /api/users?fields=id,name,manager

I get a response with resursive serialization of the manager member on each user object, like so :

[{
    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:
    {
    ...
    }
}]

I have also implemented the ability to ask for partial sub entity response, like so :

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

But it's not working due to the fact that the main object, here a user, and the sub object, the manager, are both of the same Type.

Has anyone already user NewtonJSON to implement a partial response Web API ? How do you manage to work with embedded self Type entities ?

Thank you for your advices.

도움이 되었습니까?

해결책

You could write an action filter that understands the fields parameter and converts the response. I have a sample implementation that translates the response object into a Dictionary. The keys are the fields that the client had asked and the values are the values of the fields from the object being returned in the response. The implementation is a sample and is no where near complete :)

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;
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top