1

I have a controller like this:

using Microsoft.AspNetCore.Mvc;

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

[Route("api/[controller]")]
public class ValuesController : Controller {
    [HttpGet]
    public IActionResult Get() {
        return new OkObjectResult(new[] {
            new Person { FirstName = "John", LastName = "Doe" }
        });
    }
}

I would like to be able to specify the properties I want from the response in a asp.net core REST API.
For example, a GET to api/values should return an object with all its properties:

{
  "FirstName":"John",
  "LastName":"Doe"
}

While, a GET to api/values?fields=FirstName should return only the FirstName property:

{
  "FirstName":"John"
}

I tried specifying a ContractResolver in my Startup class like this:

class QueryStringResolver : DefaultContractResolver {
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) {
        HttpContextAccessor httpContextAccessor = new HttpContextAccessor();
        string fieldsQuery = httpContextAccessor.HttpContext.Request.Query["fields"];
        if (!string.IsNullOrEmpty(fieldsQuery)) {
            var fields = from f in fieldsQuery.Split(",", StringSplitOptions.RemoveEmptyEntries) select f.Trim().ToLowerInvariant();
            return base.CreateProperties(type, memberSerialization).Where(p => fields.Contains(p.PropertyName.ToLowerInvariant())).ToList();
        } else {
            return base.CreateProperties(type, memberSerialization);
        }
    }
}

public class Startup {
    public void ConfigureServices(IServiceCollection services) {
        services
            .AddMvc()
            .AddJsonOptions(options => {
                options.SerializerSettings.ContractResolver = new QueryStringResolver();
            });
    }
    // rest of Startup class omitted...
}

The problem is that the CreateProperties method is invoked only at the first request, and not for each request.

Is there a way to specify which properties should be serialized at each request?

Please note that I don't want to change the Get method implementation or the returned class definition, I would just like to act on the serialization, so that I can re-use the same query string parameter in several methods.

Paolo Tedesco
  • 55,237
  • 33
  • 144
  • 193
  • 1
    Check this out https://stackoverflow.com/a/49672819/2373249 – hardkoded Jun 04 '18 at 14:15
  • 1
    Conditional property serialization would mean adding a ShouldSerializeXXX method for each property of each of my objects, though. – Paolo Tedesco Jun 04 '18 at 14:20
  • That would be fine in this example, but I'd rather avoid that in the real API. – Paolo Tedesco Jun 04 '18 at 14:28
  • @PaoloTedesco your problem is where you instantiated the `QueryStringResolver`. You did it on `StartUp` when you should spin it up when you need it, based on your desired result. It would make sense where you put it, but the behavior you explain states that your leveraging the resources too far up the chain. Try leveraging `QueryStringResolver ` at the APIController first and bubble it up one level at a time. – GoldBishop Jun 04 '18 at 14:49

1 Answers1

0

Create a generic object with the values you desire.

var obj = new {
  FirstName = "John"
  , LastName = "Doe"
};

var json = JsonConvert.SerializeObject(obj);
GoldBishop
  • 2,820
  • 4
  • 47
  • 82
  • I'd really prefer to act on the serialization. Moreover, I want to specify the fields in the request, and they could differ for each request. This is a compile-time solution... – Paolo Tedesco Jun 04 '18 at 14:22
  • Then handle the construction of the generic in your code. You know your conditional situation, you should handle it in the body of the controller and abstract as you need. – GoldBishop Jun 04 '18 at 14:42
  • @GoldBIshop, generic object is still not the solution here, since our class may have many fields and the client may specify any combination of fields he wants to be included, we would possibly have to end up making as many as thousands of permutations! – SDP190 May 06 '20 at 16:21
  • @SDP190 Still generic objects are the most flexible type to serialize into JSON. If you desire to have a more Concrete Type, then at least use the Generic Object to model the structure and then implement the Concrete Type. It is more important that the Sender & Receiver understand the information being transmitted and less on the approach. – GoldBishop Jun 25 '20 at 11:35
  • @GoldBishop - I think you don't understand me. When I am creating the REST Api how can I know which combination of fields a client may potentially be interested to get (lets say its a public api)? So I cant create all the types for the generic. – SDP190 Oct 12 '20 at 19:00
  • do like most public APIs and pass the requested fields either in the data package or by using query strings. – GoldBishop Oct 13 '20 at 20:03