Field level data selection in WebAPI
In this
post you will learn how to implement field level data selection functionality
in WebAPI so that client can ask for less data by sending individual columns
names through query strings variable fields.
Introduction
Service
should be smart enough to split command separated column names from query
string and convert it into object that can be used with EF query. If client sent
no column name, service should return all the columns. If you are
interested in this project code, I published on GitHub for you.
The
method which will convert comma separated column names into object should
be generic enough so that we can reuse it with any type.
So, let's
get started. First of all, let's look at the sample data we will be using to
demo this functionality.
public class StudentController : ApiController
{
// Data
source
List<Student> db = new List<Student>();
public StudentController()
{
db = new List<Student>
{
new Student { Id = 1, Name = "Abhimanyu
K Vatsa", City = "Bokaro" },
new Student { Id = 2, Name = "Deepak
Kumar Gupta", City = "Bokaro" },
new Student { Id = 3, Name = "Manish
Kumar", City = "Muzaffarpur" },
new Student { Id = 4, Name = "Rohit
Ranjan", City = "Patna" },
new Student { Id = 5, Name = "Shiv", City = "Motihari" },
new Student { Id = 6, Name = "Rajesh
Singh", City = "Dhanbad" },
new Student { Id = 7, Name = "Staya", City = "Bokaro" },
new Student { Id = 8, Name = "Samiran", City = "Chas" },
new Student { Id = 9, Name = "Rajnish", City = "Bokaro" },
new Student { Id = 10, Name = "Mahtab", City = "Dhanbad" },
};
}
// rest
codes goes here
}
In
above code you can see we have a very simple student object with pre-populated
data through constructor. You may have other data sources or you may be using
some sort of repository, in either case you are good.
Back
to basics
Now,
let's go ahead and write a very simple GET method which will return all the
data.
public IHttpActionResult Get()
{
return Ok(db);
}
So, above method will return all the records with status code 200 OK, here's the output:
You
might notice, chrome renders XML data by default and in same case IE asks to save JSON file. Basically, WebAPI can send JSON as well as XML repose and we can configure it. In my case, I just removed XML support which made JSON as
output on Chrome (above screenshot). Notice, I also reformatted the JSON response. To do this I used following code:
Now,
let's move to our original discussions.
Implementing field level data selection
I'm
going to add this functionality in above/existing GET method. So our
client will pass comma separated column names with fields query string variable like http://localhost:36140/api/student?fields=id,name
and our service will return only ID and Name data in response. If client does
not pass fields query string, service
should return all. So, here's the new GET method with all said functionality.
public IHttpActionResult Get(string fields = null)
{
try
{
List<string> lstFields = new List<string>();
if (fields != null)
{
lstFields =
fields.ToLower().Split(',').ToList();
}
//return
Ok(db.Select(i => new { i.City, i.Name }));
return Ok(db.Select(i => CreateShappedObject(i, lstFields)));
}
catch (Exception)
{
return InternalServerError();
}
}
In
above code, notice the null assigned to fields
parameter this will help to assign null in case of no query string.
In the
try block, comma separated fields in query
string is being converted into list of fields. Once we have list of fields, we
are calling a method CreateShappedObject(context,
fieldsList). This method will return response something like the one
commented which is new { i.City, i.Name }. The difference
between commented and non-commented return is commented is hardcoded with fixed
number of columns which will not solve our purpose.
So CreateShappedObject(i, lstFields) should
have a way to dynamically create objects this is where ExpandoObject comes in
action, let's look at its implementation:
public object CreateShappedObject(object obj, List<string> lstFields)
{
if (!lstFields.Any())
{
return obj;
}
else
{
ExpandoObject objectToReturn = new ExpandoObject();
foreach (var field in lstFields)
{
var fieldValue = obj.GetType()
.GetProperty(field, BindingFlags.IgnoreCase
| BindingFlags.Public | BindingFlags.Instance)
.GetValue(obj, null);
((IDictionary<string, object>)objectToReturn).Add(field,
fieldValue);
}
return objectToReturn;
}
}
In
above code, first of all we checking lstFields if its empty, return the object
as it is. If not, loop through each field in the list to add into IDictionary.
At the end, return object into EF select statement.
Look at
the different outputs it's working great in every situation.
I shared the code on GitHub also, build it on your system.
Hope this helps.
Comments
Post a Comment