1

I am implementing a protocol in ASP.NET MVC, and I need to be able to bind data from a request made like this:

curl -H "Content-Type: application/json" -d 'true' http://example.com/operation/some_id

I have tried using the [FromBody] attribute on the parameter in my controller, like this:

public ActionResult Operation(string id, [FromBody] bool setSomething)

The above code does not work, as it throws an exception when MVC attempts to set setSomething to null. If I change setSomething to a string, it always gets set to null, so I am unable to parse it to a bool in the action.

I don't have the luxury of changing 'true' to '=true' as I have read elsewhere when similar questions were asked. The POST generated from the curl command above is set in stone. What I need is some way of taking the value true (which is valid json, even without a key) from the body of the POST and assigning it to setSomething. I also need to do this in a way that doesn't prevent me from assigning some_id to id, as I already have working with a custom route.

Does anyone know how this can be accomplished in MVC or Web API?

Any help is appreciated.

Jestin
  • 330
  • 2
  • 13
  • You won't be able to use primitives because, as you say, they can never be set to `null`. There are 2 options you should try: first try using a nullable bool (so `[FromBody] bool? setSomething`) and see if that works. If it doesn't, create a custom class and have a field `setSomething` of type `bool?` in there. Can let me know if any of these work? – Jeroen Vannevel Jan 04 '15 at 01:23
  • The nullable bool behaves exactly like when I changed the parameter to a string. It no longer crashes, but the parameter is still `null`. The custom class with a bool field also doesn't crash, but the bool is always set to `false`, which is its default. I believe the problem is that there is nothing in the request to indicate what `true` gets assigned to. Thanks for the ideas. – Jestin Jan 04 '15 at 01:32
  • 1
    That will indeed be the reason. I don't know if just a body of "true" is considered valid JSON, honestly. ASP.NET will indeed not be able to parse it as JSON or at the very least know what value to bind it to. – Jeroen Vannevel Jan 04 '15 at 01:36
  • I believe it is valid JSON, since I see it all over various RESTful APIs as return values. I have never seen it as a request value, until now. Apparently, what is minimum is controversial: http://stackoverflow.com/questions/18419428/what-is-the-minimum-valid-json Either way, it's irrelevant since I cannot change what gets sent in. – Jestin Jan 04 '15 at 01:45
  • More on that point, if I use a string and change `true` to `somethingelse`, I get an ArgumentException saying 'Invalid JSON string'. It seems the JsonDeserializer that gets called accepts `true` and `false` as valid json, but not `somethingelse`. Still, this doesn't help me, but I thought anyone reading this might like to know. – Jestin Jan 04 '15 at 02:09

1 Answers1

4

I found a solution, but it's more of a workaround.

When the body of a POST is just true or false but nothing more, there is no key to bind this value to. Therefore, MVC doesn't really have anything it can do other than run the value through a JSON deserializer, which succeeds without setting any parameters in the action.

In the end, I had to read the value directly from Request, as described in MVC controller : get JSON object from HTTP body?.

I ended up using similar code:

public ActionResult Operation(string id)
{
    var req = Request.InputStream;
    req.Seek(0, SeekOrigin.Begin);
    string rawBody = new StreamReader(req).ReadToEnd();

    bool setSomething = false;

    if(bool.TryParse(rawBody, out setSomething))
    {
        // Do something with 'setSomething'
        return Json(new { id = id, status = setSomething });
    }
    throw new ArgumentException(string.Format("{0} is not a valid boolean value", rawBody));
}

As you can see, I removed setSomething from the parameter list entirely, and rely on reading the raw input stream of the request in order to get the value. This is in no way elegant, and does not make use of all the goodies we get from the MVC framework, but it works.

Community
  • 1
  • 1
Jestin
  • 330
  • 2
  • 13
  • They've over complicated this. – Ian Warburton May 20 '19 at 13:24
  • You can make model binding work by just making sure you send a name for the value you're sending. If your payload is `"setSomething":false` you're good to go. – Jeff Putz Oct 17 '20 at 19:40
  • I appreciate the suggestion after all this time. I can't even remember what I needed this for. However, like the original question said, changing anything at all about the request was an unacceptable solution. It simply wasn't on the table. Not everyone has the luxury of controlling both the server and the client, and back when I was doing .NET work, this was often the case. – Jestin Oct 17 '20 at 19:51