仅返回Web API结果中的选定字段

Con*_*nko 6 asp.net asp.net-mvc asp.net-web-api

首先,这并不是几十个其他帖子的重复,我已经尝试了所有这些帖子,但没有一个是有效的.

我有一个模型,其中包含的内容比我的web api消费者需要的更多.

public class Publication
{
    [Key]
    public int PublicationID { get; set; }
    public string PublicationTitle { get; set; }
    public string Frequency { get; set; }
    public DateTime NextIssueDate { get; set; }
    public DateTime SpaceDeadline { get; set; }
    public DateTime MaterialsDeadline { get; set; }
    public DateTime CreatedDt { get; set; }
    public string CreatedBy { get; set; }
    public DateTime UpdatedDt { get; set; }
    public string UpdatedBy { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我只想说一些要在API中传递的字段.我已经尝试过这段代码但是在Json结果中没有遗漏说UpdateBy,而是返回null值.我怎么摆脱它?我已经尝试了几十种变体,但它们要么无法编译,要么无法返回结果.

    public IQueryable<Publication> GetPublications()
    {
        return db.Publications
            .ToList()
            .Select(p => new Publication {
                PublicationID = p.PublicationID,
                PublicationTitle = p.PublicationTitle,
                Frequency = p.Frequency,
                NextIssueDate = p.NextIssueDate
            })
            .AsQueryable();
    }
Run Code Online (Sandbox Code Playgroud)

Sin*_*tic 10

不要序列化你的DAO.创建一个完整的合同,然后有选择地序列化它.要为不同的案例创建不同的合同,您可以使用Json.Net简化它; 你可以创建一个自定义合约解析器,并将其用作SerializeObject()的参数,就像这样

static void Main(string[] args)
{
    var person = new TestContract {FirstName = "John", LastName = "Doe", Age = 36};

    var firstNameContract = new SelectiveSerializer("firstname");
    var allPropertiesContract = new SelectiveSerializer("firstname, lastname, age");

    var allJson = JsonConvert.SerializeObject(
        person, 
        Formatting.Indented,
        new JsonSerializerSettings {ContractResolver = allPropertiesContract});

    var firstNameJson = JsonConvert.SerializeObject(
        person, 
        Formatting.Indented,
        new JsonSerializerSettings {ContractResolver = firstNameContract});

    Console.WriteLine(allJson);
    //  {
    //    "FirstName": "John",
    //    "LastName": "Doe",
    //    "Age": 36
    //  }


    Console.WriteLine(firstNameJson);
    //  {
    //    "FirstName": "John",    
    //  }        
}

public class SelectiveSerializer : DefaultContractResolver
{
    private readonly string[] _fields;

    public SelectiveSerializer(string fields)
    {
        var fieldColl = fields.Split(',');
        _fields = fieldColl
            .Select(f => f.ToLower().Trim())
            .ToArray();
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        property.ShouldSerialize = o => _fields.Contains(member.Name.ToLower());

        return property;
    }
}

public class TestContract
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public int Age { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

如果没有太多努力,您可以将其用于默认的mediatype格式化程序(在管道中)以在请求中查找名为"fields"或其他的参数,然后使用自定义合约解析程序(如果存在),然后它将是无缝默认如果指定则限制字段的行为,如果未指定则序列化整个对象.

在学术方面,这是正当理由:对数据的任何修改都被视为"视图关注点",这意味着,在API中,它应该由查询参数控制并接受标题.在这种情况下,数据的"表示"是application/json,您选择"过滤"返回的字段.所有这些都可以(并且应该是,imo)在序列化期间处理.因此,在这种情况下,您的"模型"将始终是完整模型与模型的某个子集.此示例中的完整模型包含名字,姓氏和年龄.实际上,这可能是数百个属性.如果要允许客户端选择完整模型的子集,则可以使用选择性序列化来完成此操作.

你可以在图形apis中使用类似的行为.在那里,大型模型的默认设置是,如果您没有指定字段,则会获得一个空对象,从而迫使客户端对其要求的内容非常具体,这在有效负载大小很重要时很有用(例如移动应用程序).并且,没有什么能阻止创建像'name'这样的字段预设,这可能意味着'firstname,lastname'或'all'包含所有属性.

我从来不喜欢拥有数百个数据对象,这些数据对象都是为20个不同的上下文中使用的数据集提供一些特殊要求,其中某些情况需要更多数据而其他情况需要更少数据.IMO如果您必须通过相同的过程来获取数据,无论是否完成,您都不应该浪费时间创建额外的对象来为客户端构建数据框架,这应该可以帮助您实现这一目标.


Cra*_* W. 7

这是因为您正在返回一组Publication对象,因此您将获得该类中包含的每个属性,无论您是否填充它.如果要返回属性的子集,则创建一个只包含要返回的属性的类,并在查询中创建该类的实例.

public IQueryable<WhatIReallyWantToReturn> GetPublications()
{
    return db.Publications
        .ToList()
        .Select(p => new WhatIReallyWantToReturn {
            PublicationID = p.PublicationID,
            PublicationTitle = p.PublicationTitle,
            Frequency = p.Frequency,
            NextIssueDate = p.NextIssueDate
        })
        .AsQueryable();
}

private class WhatIReallyWantToReturn
{
    public int PublicationID { get; set; }
    public string PublicationTitle { get; set; }
    public string Frequency { get; set; }
    public DateTime NextIssueDate { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

  • 将可查询转换为可枚举(通过 ToList()),然后再转换回可查询是没有意义的。省略 .ToList() 和 .AsQueryable(),您的结果将直接从数据库映射到新类 (2认同)