Accepting Different Types of Request Body Content In ASP..Net Web API




ASP.Net Web API is designed to accept request body in variety of ways and in different formats, however it become a real problem when you dont know how to intercept some of the requests sent to your Web API.

Recently I had a requirement to consume a third party API, they asked that I should send my request as a single Json string and also write my two endpoints to accept a single Json string. This sound simple initially but then I realized along the line that it was a bit technical.

In this article I will show you based on my experience how to intercept request in Web API.

Creating an endpoint to create customer in Web API is as simple as creating an action like so:
     [Route("customers")]
        [HttpPost]
        public HttpResponseMessage PostCustomer([FromBody] Customer customer)
        {
  
                 //Do stuff with customer object here

   //Return message 
   return Request.CreateResponse(HttpStatusCode.Created, new
                        {message = "Some respose message"
                            error = false,
                            status = "created"
                        });
           
        }
And the customer class is as simple as this:
public class Customer
    {
        public string customerName { get; set; }
        public string email { get; set; }
        public string address { get; set; }
        public string phone { get; set; }
    }


If you try making a request to this endpoint in two ways from postman like the image above, everything wil be fine, if you also send request using HttpClient like we have below, there wont be any issue at all.

async Task PostCustomer()
        {
            var customer = new Customer()
            {
                customerName = "John Doe",
                address = "Some Address somewhere",
                email = "john@doe.com",
                phone = "777-0980-22"
            };

            var client = HttpClientHelper.CreateClient("http://localhost:28349/api/v1/");
            var bodyKeyValues = new List>();

            foreach (var property in customer.GetType().GetProperties())
            {
                if (property.GetValue(customer) != null)
                {
                    bodyKeyValues.Add(new KeyValuePair(property.Name, property.GetValue(customer).ToString()));
                }
            }
            var formContent = new FormUrlEncodedContent(bodyKeyValues);

            var response = await client.PostAsync("customers", formContent);

            var json = await response.Content.ReadAsStringAsync();

            var jsonResult = JsonConvert.SerializeObject(json);
            
        }

Sending Complex Object to API

It is totally different when it is required to send some complex object to API.

Let's say we are required to send some list of orders along with a single customer to our API then we will need to have two more classes, one for product and the other will be a composite class named order for both customer and products. Let's create those.
public class Order
    {
        public Customer customer { get; set; }
        public List products { get; set; }
    }

    public class Product
    {
        public string productName { get; set; }
        public decimal price { get; set; }
    }
Let's modify the PostCustomer method to take our Order class like so:
[Route("orders")]
        [HttpPost]
        public HttpResponseMessage PostCustomer([FromBody] Order order)
        {
            
            return new HttpResponseMessage()
            {
                Content = new StringContent(JsonConvert.SerializeObject(order), Encoding.UTF8, "application/json"),
                StatusCode = HttpStatusCode.OK
            };
        }
   
There are many ways to send request to this actionresult using HttpClient. BUT WHAT HAPPENED IF YOU ARE ASKED TO SEND THE REQUEST AS A JSON STRING? Well, when I was asked to send it as a string, I did something like
[Route("orders")]
        [HttpPost]
        public HttpResponseMessage PostCustomer([FromBody] string order)
        {
            //order was always null here
            return new HttpResponseMessage()
            {
                Content = new StringContent(JsonConvert.SerializeObject(order), Encoding.UTF8, "application/json"),
                StatusCode = HttpStatusCode.OK
            };
        }
   
May be my request was not composed correctly, here is how I sent my request:
async Task PostOrder()
        {
            var _order = new Order()
            {
                customer = new Customer()
                {
                    customerName = "John Doe",
                    address = "Some Address somewhere",
                    email = "john@doe.com",
                    phone = "777-0980-22"
                },
                products = new List
                {
                    new Product()
                    {
                        price = 2000,
                        productName = "Laptop"
                    },
                    new Product()
                    {
                        productName = "Mouse",
                        price = 150
                    }
                },
            };


            var client = HttpClientHelper.CreateClient("http://localhost:28349/api/v1/");

            var jsonObj = JsonConvert.SerializeObject(_order);
            var response = await client.PostAsync("orders", new StringContent(jsonObj, Encoding.UTF32, "application/json"));
            var json = await response.Content.ReadAsStringAsync();

            var jsonResult = JsonConvert.SerializeObject(json);
           ;
        }
   
Looks like everything is fine with my request. But why am I still getting null in my API? The reason is as far as I know Web API framework tried to populate that order parameter, it uses a JsonMediaTypeFormatter. Internally its trying to do this
JsonConvert.DeserializeObject(order)
This can only work if you are sending a string literal like 'This is my request body' but since this request is a Json string it will always be null.

What is the solution?

I resolved this issue by taking advantage of HTTP. I change my method and came up with this and it worked for me.
[Route("orders")]
        [HttpPost]
        public HttpResponseMessage PostCustomer(HttpRequestMessage request)
        {
           //Read the string from request stream
            var jsonString = await request.Content.ReadAsStringAsync();
            //Deserialize it to Order object
            var order = JsonConvert.DeserializeObject(jsonString);

//Do stuff with order

            return new HttpResponseMessage()
            {
                Content = new StringContent(JsonConvert.SerializeObject(order), Encoding.UTF8, "application/json"),
                StatusCode = HttpStatusCode.OK
            };
        }
   
I hope this helps. If you have faced this issue as a developer, please let me know how it was resolved.

No comments

Powered by Blogger.