How to receive JSON object in POST and GET using web api
ASP.NET Web API is a great tool for building
HTTP services with ASP.NET. It makes many things HTTP that used to be ill
defined in .NET easy and allows you to build a variety of services very easily.
Like other complex abstraction frameworks it makes complex scenarios super
easy, while some very simple and common operations are not quite as straight
forward or as transparent as they should be.
Capturing the raw Request Body
But things are not so easy and transparent when you want to pass simple
parameters like strings, numbers, dates etc. Because Web API is based on a host
of conventions, some things that should be easy, such as easily picking up the
raw content of a POST or PUT operation and capturing it, aren't quite so
transparent as the automatic type mapping shown above.
One thing you might expect to be able to easily do, is to pick up the
entire content body and map it to a parameter like this:
[HttpPost]
public string PostRawBuffer(string raw)
{
return raw;
}
Quick, what do you think is required by the client to call this method
the string parameter? I’ll wait here while you ponder…
The answer is - not
easily and not without some additional ‘hints’.
There are a number of issues here
that actually make this one of the worst parameter signatures in Web API.
This parameter signature does
not work with any of these posted values:
- Raw Content Buffer Data (entire buffer)
- A JSON string with application/json content type
- A UrlEncoded Form Variable
- A QueryString Variable
In fact, no matter what you pass here in posted body content – the
string parameter is always null. The same is true if you have any other simple
parameter type – numbers, bools, dates, byte[] etc. Plain parameter mapping
(without special attributes) works only complex types like objects and arrays.
If you really think about how Web API’s parameter bindings work, this
sort of makes sense with the exception of the JSON string parameter. Parameter
bindings map based on media types (ie. content-type header) using the Coning
algorithm by default and try to map parameters as whole entities. If you post a
a raw string or buffer Web API internally really has no idea how to map this to
anything. Should it map the entire buffer? A form variable? A JSON string? So
it needs some hints to do it’s thing.
[FromBody]
to retrieve Content
I mentioned hints, and Web
API allows you to use parameter binding attributes to provide these hints. These
allow you to tell Web API where the content is coming from explicitly. There
are [FromBody] and [FromUri] attributes that can force content to be mapped
from POST or query string content for example.
Using [FromBody] like this:
[HttpGet]
[Route("GetProduct")]
public async Task<IActionResult> GetProduct([FromBody] JObject Productsearch)
{
return StatusCode(200, Productsearch);
}
Now allows JSON or XML
content to be mapped from the body. Same goes for other simple parameter types like numbers, dates,
bools etc.
Now we have Create web API That code is below:
First Of We create
Service Response Class for set Response Structure of web api
ServiceResponse.cs
public class ServiceResponse
{
public ServiceResponse()
{
this.IsSuccess = true;
this.Errors = new List<string>();
}
public ServiceResponse(string
errorCode)
{
this.IsSuccess = false;
this.Errors.Add(errorCode);
}
public List<string>
Errors { get; set; }
public bool
IsSuccess { get; set; }
public object Data { get; set; }
public void
AddError(string error)
{
this.IsSuccess = false;
this.Errors.Add(error);
}
public void
AddErrorList(List<string>
Error) {
this.IsSuccess = false;
this.Errors.AddRange(Error);
}
}
And now we create Model of
the product
public class Product
{
public long Id { get; set; }
public string Name { get; set; }
public string Brand { get; set; }
public decimal Price { get; set; }
public string Category
{ get; set; }
public string Image { get; set; }
}
Now we create
SortingExtenson static class that Contain Common Code For Dynamic Orderby, Get
value From json Object
SortingExtension.cs
// Create
dynamic Orderby just Pass IQueryble Querya Column name And descending
public static
IQueryable<T> OrderByDynamic<T>(this
IQueryable<T> query, string sortColumn, bool
descending)
{
// Dynamically
creates a call like this: query.OrderBy(p => p.SortColumn)
var parameter = Expression.Parameter(typeof(T), "p");
string command = "OrderBy";
if (descending)
{
command = "OrderByDescending";
}
Expression resultExpression = null;
var property = typeof(T).GetProperty(sortColumn);
// this is the
part p.SortColumn
var propertyAccess = Expression.MakeMemberAccess(parameter,
property);
// this is the
part p => p.SortColumn
var orderByExpression = Expression.Lambda(propertyAccess, parameter);
// finally,
call the "OrderBy" / "OrderByDescending" method with the
order by lamba expression
resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { typeof(T), property.PropertyType },
query.Expression,
Expression.Quote(orderByExpression));
return query.Provider.CreateQuery<T>(resultExpression);
}
// get All Key
from Json object
public static void GetKeys(JObject obj, List<string> keys)
{
var result = obj.Descendants()
.Where(f => f is JProperty) //.Where(f => f is JProperty)
//.Select(f
=> f as JProperty)// and .Select(f => f as JProperty) can be replaced
with .OfType<JProperty>()
.OfType<JProperty>()
.Select(f => f.Name)
.Where(f =>
!keys.Contains(f));
keys.AddRange(result);
}
// Check json
Key List into Db Table key is exist or not in Db
public static void CheckKeyInDb<T>(List<string> Keys, List<string> Error)
{
for (int i = 0; i
< Keys.Count; i++)
{
if (typeof(T).GetProperty(Keys[i].ToString())
== null)
{
Error.Add(Keys[i].ToString() + "
Parameter is Not Valid");
}
}
}
// Get Value
from JsonObject By Parse List Of key and Store into List Same Index of Key
public static void GetValuefromJsononject(JObject obj,
List<string> Keys, List<string> Value)
{
for (int i = 0; i
< Keys.Count(); i++)
{
Value.Add(obj[Keys[i].ToString()] != null ? obj[Keys[i].ToString()].ToString() : "");
}
}
// Get Value
from JsonObject By Parse List Of key and Store Into dictionary key and value
paire
public static void GetValuefromJsononject(JObject obj,
List<string> Keys, Dictionary<string, string> Value)
{
for (int i = 0; i
< Keys.Count(); i++)
{
Value.Add(Keys[i].ToString(),
obj[Keys[i].ToString()] != null ? obj[Keys[i].ToString()].ToString() : "");
}
}
Now we create Interface that contain definition of get
data from Database
IProductService.cs
Task<List<Product>>
GetProducts(Dictionary<string, string> filter, int skip, int limit, string sortField, bool descending);
Now we create Productservice that inherit IProductService interface
private readonly IProductRepository _productRepository;
private readonly UsersDbContext _dbContext;
// initialize
UserDbContext And IProductRepository object
public ProductService(IProductRepository productRepository, UsersDbContext
usersDbContext)
{
this._productRepository = productRepository;
_dbContext = usersDbContext;
}
public async
Task<List<Product>> GetProducts(Dictionary<string, string> filter, int skip =
0, int limit = 10, string sortField = "Name", bool descending = false)
{
List<Product> lstProduct = new List<Product>();
var productList = _dbContext.Products.AsQueryable();
foreach (var d in filter)
{
string colName = d.Key;
string colValue = d.Value;
switch (colName.ToLower())
{
case "name":
productList =
productList.Where(p => p.Name.Contains(colValue));
break;
case "brand":
productList =
productList.Where(p => p.Brand == colValue);
break;
case "price":
productList =
productList.Where(p => p.Price <= Convert.ToDecimal(colValue));
break;
case "category":
productList = productList.Where(p =>
p.Category == colValue);
break;
case "id":
productList =
productList.Where(p => p.Id == Convert.ToInt64(colValue));
break;
default:
break;
}
}
productList =
SortingExtension.OrderByDynamic<Product>(productList, sortField,
descending);
productList = productList.Skip(skip).Take(limit);
return lstProduct = await productList.ToListAsync();
}
And last We Create
Controller that handle the Request calling From Front-end or Postman
Controller.cs
[HttpGet]
[Route("GetProduct")]
public async
Task<IActionResult> GetProduct([FromBody] JObject Productsearch)
{
ServiceResponse response = new ServiceResponse();
try
{
int Skip = 0, Limit = 10;
bool descending = true;
List<string>
Filterkey = new List<string>();
List<string> Sortkey = new List<string>();
List<string> Error = new List<string>();
Dictionary<string, string> FilterValue = new Dictionary<string, string>();
if (Productsearch != null)
{
var detail = Productsearch["data"];
if (detail != null && detail["filter"] != null)
{
SortingExtension.GetKeys((JObject)detail["filter"], Filterkey);
}
else
{
response.AddError("Filter List Required");
}
if (detail["sort"] != null)
{
SortingExtension.GetKeys((JObject)detail["sort"], Sortkey);
SortingExtension.CheckKeyInDb<Product>(Sortkey, Error);
if (Error.Count == 0)
{
if (detail["sort"][Sortkey[0]].ToString().ToLower()
== "asc")
{
descending = false;
}
}
else
{
response.AddErrorList(Error);
}
}
if (detail["paging"] != null)
{
Skip = detail["paging"]["skip"] != null ? Convert.ToInt32(detail["paging"]["skip"].ToString()) : 0;
Limit = detail["paging"]["limit"] != null ? Convert.ToInt32(detail["paging"]["limit"].ToString()) : 10;
}
SortingExtension.GetValuefromJsononject((JObject)detail["filter"], Filterkey,
FilterValue);
string sort = Sortkey.Count > 0 ?
Sortkey[0].ToString() : "Name";
response.Data = await _productService.GetProducts(FilterValue,
Skip, Limit, sort, descending);
}
}
catch (Exception ex)
{
response.AddError(ex.ToString());
}
return StatusCode(200, response);
}
Now we have testing the
code from Postman
Passing Parameter from body
Json Raw Data here is Sample Json Data That pass in Request.
{
"data": {
"filter": {
"Name": "Coffee"
},
"sort": {
"Brand": "asc"
},
"paging": {
"skip": 10,
"limit": 20
}
}
}
Comments
Post a Comment