Web Api用户过滤响应字段

时间:2014-08-13 06:23:32

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

我正在创建一个新的Web API,并希望允许用户指定在URL中返回哪些字段。

我目前的想法是:

对于这样的样本模型:

public class Value
{
    public string ValueId { get; set; }

    public int Number { get; set; }

    public ValueInternal Internal { get; set; }
}
public class ValueInternal
{
    public int Number { get; set; }

    public string Something { get; set; }
}

和这样的网址

http://example.com/api/values/?_fields=Number,Internal(Something)

会返回此

[
   {
       "Number": 0,
       "Internal": {
           "Number": 0
       }
   }
]

我已经提出了以下实现此目的的方法,但它有一些缺陷。即如果InternalValueInternal的可枚举或者不支持包括所有或包含所有除外,或者如果T和TResult是不同类型,则无法处理。有没有人对我如何改进这一点有任何建议,或者是否已经有一种方法可以解决这个问题。

public static Expression<Func<T, TResult>> CreateSelector<T, TResult>() where TResult : new()
    {
        var property = "Number,Internal(Something)";
        return arg => Process<T, TResult>(arg, default(TResult), property);
    }

    private static TResult Process<T, TResult>(T arg, TResult output, string propertyList) where TResult : new()
    {
        if (output == null)
        {
            output = new TResult();
        }
        if (string.IsNullOrEmpty(propertyList))
        {
            return output;
        }
        var properties = Regex.Split(propertyList, @"(?<!,[^(]+\([^)]+),");
        foreach (var property in properties)
        {
            var propertyName = property;
            var propertyInternalsMatch = Regex.Match(property, @"\(.*(?<!,[^(]+\([^)]+)\)");
            var internalPropertyList = propertyInternalsMatch.Value;
            if (!string.IsNullOrEmpty(internalPropertyList))
            {
                propertyName = property.Replace(internalPropertyList, "");
                internalPropertyList = internalPropertyList.Replace("(", "");
                internalPropertyList = internalPropertyList.Replace(")", "");
            }
            var tProperty = arg.GetType().GetProperty(propertyName);
            if(tProperty == null) continue;
            var tResultProperty = output.GetType().GetProperty(propertyName);
            if(tResultProperty == null) continue;

            if (tProperty.PropertyType.IsPrimitive || tProperty.PropertyType.IsValueType || (tProperty.PropertyType == typeof(string)))
            {
                tResultProperty.SetValue(output, tProperty.GetValue(arg));
            }
            else
            {
                var propertyInstance = Activator.CreateInstance(tResultProperty.PropertyType);
                tResultProperty.SetValue(output, Process(tProperty.GetValue(arg), propertyInstance, internalPropertyList));
            }
        }

        return output;
    }

经过多一点阅读之后,我想我想做一些像这个问题LINQ : Dynamic select的答案,但是我的解决方案仍有同样的缺陷

1 个答案:

答案 0 :(得分:2)

如果您在ASP .NET Web API上使用OData支持,则可以使用$select,但如果您不想使用它,或者使用Linq无法轻松查询底层系统,则可以使用自定义合约解析程序,但在这种情况下,您只是减少序列化大小,而不是内部数据流量。

public class FieldsSelectContractResolver : CamelCasePropertyNamesContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        property.GetIsSpecified = (t) =>
        {
        var fields = HttpContext.Current.Request["fields"];

        if (fields != null)
        {
            return fields.IndexOf(member.Name, StringComparison.OrdinalIgnoreCase) > -1;
        }

        return true;
        };

        return property;
    }
}

并在WebApiConfig.cs中设置自定义合约解析程序:

var jsonFormatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
jsonFormatter.SerializerSettings.ContractResolver = new FieldsSelectContractResolver();