35

Why is the parameter always null when I call the below Post method with the below ajax?

public IEnumerable<string> Post([FromBody]string value)
{
    return new string[] { "value1", "value2", value };
}

Here is the call to the Web API method via ajax:

  function SearchText() {
        $("#txtSearch").autocomplete({
            source: function (request, response) {
                $.ajax({
                    type: "POST",
                    contentType: "application/json; charset=utf-8",
                    url: "api/search/",
                    data: "test",
                    dataType: "text",
                    success: function (data) {
                        response(data.d);
                    },
                    error: function (result) {
                        alert("Error");
                    }
                });
            }
        });
    }
Ryan Gates
  • 4,501
  • 6
  • 50
  • 90
daniel
  • 34,281
  • 39
  • 104
  • 158

4 Answers4

60
$.ajax({
    url: '/api/search',
    type: 'POST',
    contentType: 'application/x-www-form-urlencoded; charset=utf-8',
    data: '=' + encodeURIComponent(request.term),
    success: function (data) {
        response(data.d);
    },
    error: function (result) {
        alert('Error');
    }
});

Basically you can have only one parameter of scalar type which is decorated with the [FromBody] attribute and your request needs to use application/x-www-form-urlencoded and the POST payload should look like this:

=somevalue

Notice that contrary to standard protocols the parameter name is missing. You are sending only the value.

You can read more about how model binding in the Web Api works in this article.

But of course this hacking around is a sick thing. You should use a view model:

public class MyViewModel
{
    public string Value { get; set; }
}

and then get rid of the [FromBody] attribute:

public IEnumerable<string> Post(MyViewModel model)
{
    return new string[] { "value1", "value2", model.Value };
}

and then use a JSON request:

$.ajax({
    url: '/api/search',
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    data: JSON.stringify({ value: request.term }),
    success: function (data) {
        response(data.d);
    },
    error: function (result) {
        alert('Error');
    }
});
Jaime Torres
  • 10,365
  • 1
  • 48
  • 56
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 1
    Very useful part: POST payload to be in this form '=somevalue' ... I got crazy trying to figure out why is not working for me. I had it in the form 'key=value'. So THANKS! – Learner Mar 10 '14 at 21:16
  • The upper solution with the '=' + is good hack, but you don't need to specify the content type for that as the default ajax content type is "contentType: 'application/x-www-form-urlencoded; charset=utf-8'," :] – dlght Sep 26 '14 at 12:00
14

You cannot use a simple type for the [FromBody] attribute with the JSON content type. Although the default in Visual Studio has a string from body this is for the application/x-www-form-urlencoded content type.

Put the string value as a property on a basic model class and the deserialiser will work.

public class SimpleModel()
{
    public string Value {get;set;}
}

public IEnumerable<string> Post([FromBody]SimpleModel model)
{
    return new string[] { "value1", "value2", model.Value };
}

Change the JSON you are sending to:

{"Value":"test"}
redman
  • 1,510
  • 17
  • 33
Mark Jones
  • 12,156
  • 2
  • 50
  • 62
  • why Visual Studio uses string as default then? – daniel Jan 31 '13 at 11:19
  • Clarification added. The Values one would work with the application/x-www-form-urlencoded content type... but you are using application/json. – Mark Jones Jan 31 '13 at 11:23
  • model is still null, also with datatype json – daniel Jan 31 '13 at 11:26
  • Between mine and Darin's post you should have your answer. If you want to use JSON you need the Model approach if you rather post just a string then change the contentType and format the data property as per Darin's example. – Mark Jones Jan 31 '13 at 11:29
1

whenever we are calling web api action and which take [frombody] parameter then input parameter prefix with = for example

public string GetActiveEvents([FromBody] string XMLRequestString) {
}

to call above web api action

  1. URI

  2. 2.

User-Agent: Fiddler

Content-Type: application/x-www-form-urlencoded

Host: localhost:54702

Content-Length: 936

  1. request body is =data

I hope this will give clear idea.

Sruit A.Suk
  • 7,073
  • 7
  • 61
  • 71
1

I've just had a beast of a time with this and .NET Core Web API. So hopefully to save time for someone: The actual problem for me was simple - I wasn't converting to the correct type (Notice @Darins answer uses a VM rather than a string).

The default type in the template is string. I thought because we're sending stringified JSON, we would see a JSON string, but this was not the case. I had to make it the correct type.

E.g. This failed

[EnableCors("AllowAll")]
[HttpPost]
public HttpResponseMessage Post([FromBody]string value)
{
    // Do something with the blog here....

    var msg = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
    return msg;

}

But this worked.

[EnableCors("AllowAll")]
[HttpPost]
public HttpResponseMessage Post([FromBody]Blog value)
{
    // Do something with the blog here....

   var msg = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
  return msg;

}

Ajax Call

function HandleClick() {

    // Warning - ID's should be hidden in a real application 
    //         - or have covering GUIDs.
    var entityData = {
        "blogId": 2,
        "url": "http://myblog.com/blog1",
        "posts": [
        {
            "postId": 3,
            "title": "Post 1-1",
            "content": "This is post 1 for blog 1",
            "blogId": 2
        },
        {
            "postId": 4,
            "title": "Post 1-2",
            "content": "This is post 2 for blog 1",
            "blogId": 2
        }
        ]
    };


    $.ajax({
        type: "POST",
        url: "http://localhost:64633/api/blogs",
        async: true,
        cache: false,
        crossDomain: true,
        data: JSON.stringify(entityData),
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (responseData, textStatus, jqXHR) {
            var value = responseData;
        },
        error: function (responseData, textStatus, errorThrown) {
            alert('POST failed.');
        }
    });

}
JsAndDotNet
  • 16,260
  • 18
  • 100
  • 123