1

I have a WebAPI service where I would like to allow users to specify which fields they'd like returned. For example, say I have the following Person class:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
}

and that calling /api/people/ returns all three fields for all people. How do I handle a request like /api/people?fields=FirstName,Email returning just those two fields for all people? Bonus points if I can map something like first_name to FirstName but that's not required.

6
  • 4
    I believe you are looking for OData Commented Feb 8, 2018 at 13:34
  • @CamiloTerevinto correct me if I'm wrong but I think OData is in alpha stage right? Commented Feb 8, 2018 at 13:44
  • @EdwardChopuryan It could be, haven't really used that for ASP.NET Core Commented Feb 8, 2018 at 13:46
  • It looks like OData has the functionality via the $select property however Core support is spotty at best. Commented Feb 8, 2018 at 13:47
  • 1
    @PhillipCopley yep it is in alpha stage. If it bothers you you have to write your own controller. It shouldn't be that hard. Commented Feb 8, 2018 at 13:48

2 Answers 2

3

This is a job for the Dynamic Language Runtime and specifically ExpandoObject, whereby you return only the properties you need as determined at runtime:

public dynamic GetPerson()
{
    bool firstNameRequired = true; // TODO: Parse querystring
    bool lastNameRequired = false; // TODO: Parse querystring

    dynamic rtn = new ExpandoObject();

    if (firstNameRequired)
        rtn.first_name = "Steve";

    if (lastNameRequired)
        rtn.last_name = "Jobs";

    // ... and so on

    return rtn;
}

void Main()
{
    // Using the serializer of your choice:
    Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(GetPerson()));
}

Output:

{"first_name":"Steve"}

I don't have the means to test it right now [I have something similar in production on vanilla Web API, with a large number of optional fields], but going by the .NET Core docs the web method would look something like this albeit without the hard coded values!:

[HttpGet()]
public IActionResult Get([FromQuery(Name = "fields")] string fields)
{
    var fieldsOptions = fields.Split(',');

    dynamic rtn = new ExpandoObject();

    if (fieldsOptions.Contains("FirstName"))
        rtn.first_name = "Steve";

    if (fieldsOptions.Contains("LastName"))
        rtn.last_name = "Jobs";

    if (fieldsOptions.Contains("Email"))
        rtn.email = "[email protected]";

    return new ObjectResult(rtn);
}

You would need to reference the System.Dynamic.Runtime package.

Sign up to request clarification or add additional context in comments.

Comments

1

You can use conditional serialization. You will just base your ShouldSerialize return value on whether or not the fields parameter contains the string identifying that field. This will work for whatever string(s) you want to associate with the field. You can even accept multiple strings for each field, if you want to.

To get the fieldList into the object you want to serialize (Person), you can just pass it or something that contains it (HttpRequest.Query or some custom Options class) into the constructor.

public bool ShouldSerializeFirstName(){  
    return fieldList.Contains("FirstName");            
}

5 Comments

Wouldn't this tightly couple you to NewtonSoft's json serialiser? How would you go about injecting the field list from the query string?
But yes, if you wanted this to work with a custom serlializer, it would have to support conditional serialization consistent with Json.NET .
This is a neat concept and I'm hovering over the +1 button, but you don't appear to have answered @Mardoxx 's question "How would you go about injecting the field list from the query string?". It looks like ShouldSerializeFirstName would need to be defined in the Person class, and therefore you'd have to use the static HttpContext.Current property in order to get the request details. I'm not sure I like having that dependency in my domain objects unless they're strictly web facing only. At the very least, if my assessment is correct, you should return true if HttpContext.Current is null
I'm not seeing anywhere that he says Person is a domain object. And I think how to decouple a WebApi DTO from from a domain object is outside the scope of this question. If he does have that need, creating a wrapper in the application layer PersonDTO with the serialization members on it would be a simple solution. The wrapper could just pass through common data from the Person object get{return base.FirstName;}, etc
@StephenKennedy I've address some of your concerns in an edit.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.