7

Is there a way for me to receive a json object as a string and not convert to a class in my Web API?

If I create a class that matches the structure of the json object I receive, everything works nicely because the ModelBinder handles it. However, if I try to keep it as a string, it's failing. The value of transaction parameter ends up being null -- see my API method below.

This is what my CreditCardTransaction class looks like that I use to receive data into my API method:

public class CreditCardTransaction
{
   public int Amount { get; set; }
   public string Description { get; set; }
   public string Token { get; set; } // This is the json object
}

And my API method looks like this:

[HttpPost("process")]
public async Task<IActionResult> ProcessPayment([FromBody] CreditCardTransaction transaction)
{
   if(transaction == null || !ModelState.IsValid)
      return new StatusCodeResult(400);

   // Process transaction
}

I'm doing this for Stripe Checkout integration where Stripe frontend client converts sensitive credit card data into what they call a token which is a json object. So, in my backend API, I just want to receive the token along with a couple of simple data points i.e. amount and description.

If I set my CreditCardTransaction class to the above structure, transaction ends up getting a null value. Looks like ModelBinder doesn't like the idea of having a json object in Token property which is defined as a string.

If I create a class that matches the structure of the json object for the token, I'm able to receive it but it's causing other issues for me, not to mention all the unnecessary conversions.

I do NOT need the token except to pass it to Stripe's API to complete the transaction. I will not save it or manipulate it. Looks like Stripe's API is expecting a json object anyway so I simply want to receive it and pass it to their API as is without messing with it.

How do I receive this json object?

Sam
  • 26,817
  • 58
  • 206
  • 383
  • 1
    Did you try using the dynamic keyword? You can get an idea here about the usage of dynamic keyword. There is no need to define a POCO class. http://www.carlserver.com/blog/post/using-c%23-dynamic-keyword-to-replace-data-transfer-objects – Baskar Rao Dec 15 '17 at 17:19
  • Sorry I am a bit confused... is Task a standard object? Or can you show the definition – Grantly Dec 15 '17 at 17:20
  • @Baskar Yes! dynamic worked! Thank you. Please post it as an answer so that I can accept it. – Sam Dec 15 '17 at 17:22
  • 2
    Just declare `Token` to be a `JToken` or `JRaw`. See [Get Raw json string in Newtonsoft.Json Library](https://stackoverflow.com/q/10757450/3744182). – dbc Dec 15 '17 at 17:25
  • @dbc I think this is a good approach too but it requires me to create a class that matches the structure of the `json` object. I'd rather not even mess with it. Thank you for your suggestion though. – Sam Dec 15 '17 at 17:31
  • Possible duplicate of [ASP.NET Core MVC : How to get raw JSON bound to a string without a type?](https://stackoverflow.com/questions/31952002/asp-net-core-mvc-how-to-get-raw-json-bound-to-a-string-without-a-type) – Sevenate Jul 23 '19 at 21:57
  • Try using the dynamic keyword? You can get an idea here about the usage of dynamic keyword. There is no need to define a POCO class. http://www.carlserver.com/blog/post/using-c%23-dynamic-keyword-to-replace-data-transfer-objects – Baskar Rao Dec 15 '17 at 17:23
  • Actually, `dynamic` is not doing the trick. I got excited because I was capturing the values but then I end up performing a lot of tricks to convert `dynamic` into a `jObject`. I really should be able to receive `string` but for some reason it's not working. – Sam Dec 15 '17 at 20:18
  • I'm back to using the `dynamic` option. Once I get the data in a `dynamic` object, I serialize it using Newtonsoft `JsonConvert.SerializeObject()`. This seems to be working but seems unnecessarily complex. – Sam Dec 15 '17 at 21:15

1 Answers1

6

Just read from Request.Body stream:

public async Task<IActionResult> ProcessPayment()
{
    using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
    {
            string rawValue = await reader.ReadToEndAsync();
    }
    ...
}

Updated: the following doesn't work with ASP.NET Core 2.0.

Actually, the string type for parameter should work if you post with Content-Type: application/json:

public async Task<IActionResult> ProcessPayment([FromBody] string transaction)
{
    ...
}

There is a good article about all these things: Accepting Raw Request Body Content in ASP.NET Core API Controllers

Set
  • 47,577
  • 22
  • 132
  • 150
  • 2
    The `Content-Type` is already set to `application/json` but for some reason, if I try to receive `string`, it's not working. I end up getting a `null` value in `transaction` parameter. – Sam Dec 15 '17 at 20:17
  • 1
    @Sam yeh, looks like the second option doesn't work with ASP.NET Core 2.0... Thought reading directly from `Request.Body` is working – Set Dec 15 '17 at 21:12
  • 1
    I had to go back to the `dynamic` option. I then serialize the object using `JsonConvert.SerializeObject()`. Seems so unnecessary! – Sam Dec 15 '17 at 21:14
  • 1
    unncessary is the name of the game when it comes to .net core. Takes a week to just get to where express init starts you off. – mrsmarts Mar 16 '20 at 16:35