关于javascript:如何将json POST数据作为对象传递给Web API方法?

How to pass json POST data to Web API method as an object?

ASP.NET MVC4 Web API应用程序定义了保存客户的Post方法。在Post请求主体中以JSON格式传递客户。Post方法中的客户参数包含属性的空值。

如何修复此问题,以便将已发布的数据作为客户对象传递?

如果可能的话,应该使用内容类型:application/x-www-form-urlencoded,因为我不知道如何在发布表单的javascript方法中更改它。

控制器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class CustomersController : ApiController {

  public object Post([FromBody] Customer customer)
        {
            return Request.CreateResponse(HttpStatusCode.OK,
            new
            {
                customer = customer
            });
        }
    }
}

public class Customer
    {
        public string company_name { get; set; }
        public string contact_name { get; set; }
     }

请求:

1
2
3
4
5
6
POST http://localhost:52216/api/customers HTTP/1.1
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded; charset=UTF-8

{"contact_name":"sdfsd","company_name":"ssssd"}

编辑:2017年10月31日

同样的代码/方法也适用于ASP.NET核心2.0。主要的区别在于,在ASP.NET核心中,Web API控制器和MVC控制器都被合并到单个控制器模型中。所以您的返回类型可能是IActionResult或它的一个实现(例如:OkObjectResult)

使用

1
contentType:"application/json"

发送时需要使用JSON.stringify方法将其转换为json字符串,

模型绑定器将JSON数据绑定到类对象。

以下代码可以正常工作(已测试)

1
2
3
4
5
6
7
8
9
$(function () {
    var customer = {contact_name :"Scott",company_name:"HP"};
    $.ajax({
        type:"POST",
        data :JSON.stringify(customer),
        url:"api/Customer",
        contentType:"application/json"
    });
});

结果

enter image description here

contentType属性告诉服务器我们正在以JSON格式发送数据。因为我们发送了一个JSON数据结构,所以模型绑定将正常进行。

如果检查Ajax请求的头,可以看到Content-Type值设置为application/json

如果不显式指定ContentType,它将使用默认的内容类型,即application/x-www-form-urlencoded;

2015年11月编辑,以解决评论中提出的其他可能问题。

发布复杂对象

假设有一个复杂的视图模型类作为Web API操作方法参数,如下所示

1
2
3
4
5
6
7
8
9
10
11
public class CreateUserViewModel
{
   public int Id {set;get;}
   public string Name {set;get;}  
   public List<TagViewModel> Tags {set;get;}
}
public class TagViewModel
{
  public int Id {set;get;}
  public string Code {set;get;}
}

你的Web API的终点是

1
2
3
4
5
6
7
8
9
10
11
public class ProductController : Controller
{
    [HttpPost]
    public CreateUserViewMode Save([FromBody] CreateUserViewModel m)
    {
        // I am just returning the posted model as it is.
        // You may do other stuff and return different response.
        // Ex : missileService.LaunchMissile(m);
        return m;
    }
}

在编写本文时,ASP.NET MVC 6是最新的稳定版本,在MVC6中,Web API控制器和MVC控制器都继承自Microsoft.AspNet.Mvc.Controller基类。

要从客户端向方法发送数据,下面的代码应该可以正常工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Build an object which matches the structure of our view model class
var model = {
    Name:"Shyju",
    Id: 123,
    Tags: [{ Id: 12, Code:"C" }, { Id: 33, Code:"Swift" }]
};

$.ajax({
    type:"POST",
    data: JSON.stringify(model),
    url:"../product/save",
    contentType:"application/json"
}).done(function(res) {      
    console.log('res', res);
    // Do something with the result :)
});

模型绑定适用于某些属性,但并非所有属性!为什么?

如果不使用[FromBody]属性修饰web api方法参数

1
2
3
4
5
[HttpPost]
public CreateUserViewModel Save(CreateUserViewModel m)
{
    return m;
}

在不指定ContentType属性值的情况下发送模型(原始javascript对象,而不是JSON格式)

1
2
3
4
5
6
7
$.ajax({
    type:"POST",
    data: model,
    url:"../product/save"
}).done(function (res) {
     console.log('res', res);
});

模型绑定将适用于模型上的平面属性,而不是类型复杂/其他类型的属性。在我们的例子中,IdName属性将适当地绑定到参数m上,但Tags属性将是一个空列表。

如果您使用的是短版本$.post,则会出现同样的问题,该版本在发送请求时将使用默认的内容类型。

1
2
3
4
$.post("../product/save", model, function (res) {
    //res contains the markup returned by the partial view
    console.log('res', res);
});


在webapi中使用post可能很棘手!希望添加到已经正确的答案中。

将重点放在post上,因为处理get是微不足道的。我不认为有多少人会到处寻找用webapis解决get问题的方法。不管怎样…

如果您的问题是-在MVC Web API中,如何-使用通用HTTP谓词以外的自定义操作方法名称?-执行多个职位?-发布多个简单类型?-通过jquery发布复杂类型?

那么,以下解决方案可能会有所帮助:

首先,要在Web API中使用自定义操作方法,请将Web API路由添加为:

1
2
3
4
5
6
public static void Register(HttpConfiguration config)
{
    config.Routes.MapHttpRoute(
        name:"ActionApi",
        routeTemplate:"api/{controller}/{action}");
}

然后您可以创建如下操作方法:

1
2
3
4
5
[HttpPost]
public string TestMethod([FromBody]string value)
{
    return"Hello from http post web api controller:" + value;
}

现在,从浏览器控制台启动以下jquery

1
2
3
4
5
6
7
8
$.ajax({
    type: 'POST',
    url: 'http://localhost:33649/api/TestApi/TestMethod',
    data: {'':'hello'},
    contentType: 'application/x-www-form-urlencoded',
    dataType: 'json',
    success: function(data){ console.log(data) }
});

第二,要执行多个日志,它很简单,创建多个操作方法,并用[httppost]attrib进行修饰。使用[actionname("myaction")]来分配自定义名称等。将在下面的第四点进入jquery。

第三,首先,在一个操作中发布多个简单类型是不可能的。此外,还有一种特殊的格式可以发布一个简单的类型(除了在查询字符串或REST样式中传递参数)。这正是我与REST客户机(如Fiddler和Chrome的高级REST客户机扩展)发生冲突并在网络上搜索了近5个小时的原因,最终,以下URL证明是有帮助的。将引用链接的相关内容可能会失效!

1
2
3
Content-Type: application/x-www-form-urlencoded
in the request header and add a = before the JSON statement:
={"Name":"Turbo Tina","Email":"[email protected]"}

附言:注意到特殊的语法了吗?

http://forums.asp.net/t/1883467.aspx?当+i+try+to+post+to+my+web+api时,+received+value+为+null+

不管怎样,让我们把那个故事讲清楚。继续前进:

第四,通过jquery发布复杂类型,当然,$.ajax()将很快成为角色:

假设action方法接受一个具有ID和名称的Person对象。因此,从javascript:

1
2
3
4
5
6
7
8
9
var person = { PersonId:1, Name:"James" }
$.ajax({
    type: 'POST',
    url: 'http://mydomain/api/TestApi/TestMethod',
    data: JSON.stringify(person),
    contentType: 'application/json; charset=utf-8',
    dataType: 'json',
    success: function(data){ console.log(data) }
});

这个动作看起来像:

1
2
3
4
5
[HttpPost]
public string TestMethod(Person person)
{
    return"Hello from http post web api controller:" + person.Name;
}

所有这些,都为我工作!干杯!


我只是在玩这个,发现了一个相当奇怪的结果。假设您在C中的类上有公共属性,如下所示:

1
2
3
4
5
public class Customer
{
    public string contact_name;
    public string company_name;
}

然后您必须按照shyju的建议执行json.stringify技巧,并按如下方式调用它:

1
2
3
4
5
6
7
var customer = {contact_name :"Scott",company_name:"HP"};
$.ajax({
    type:"POST",
    data :JSON.stringify(customer),
    url:"api/Customer",
    contentType:"application/json"
});

但是,如果您像这样在类上定义getter和setter:

1
2
3
4
5
public class Customer
{
    public string contact_name { get; set; }
    public string company_name { get; set; }
}

然后你可以更简单地称之为:

1
2
3
4
5
$.ajax({
    type:"POST",
    data :customer,
    url:"api/Customer"
});

这将使用HTTP头:

10

我不太确定这里发生了什么,但它看起来像一个bug(特性?)在框架中。假设不同的绑定方法调用不同的"适配器",而application/json-one的适配器使用公共属性,而表单编码数据的适配器则不使用公共属性。

不过,我不知道哪一个是最佳实践。


使用json.stringify()获取json格式的字符串,确保在进行Ajax调用时传递以下属性:

  • contenttype:'application/json'
  • 数据类型:"json"

下面是用于对ASP.NET Web API进行Ajax Post调用的give jquery代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var product =
    JSON.stringify({
        productGroup:"Fablet",
        productId: 1,
        productName:"Lumia 1525 64 GB",
        sellingPrice: 700
    });

$.ajax({
    URL: 'http://localhost/api/Products',
    type: 'POST',
    contentType: 'application/json',
    dataType: 'json',
    data: product,
    success: function (data, status, xhr) {
        alert('Success!');
    },
    error: function (xhr, status, error) {
        alert('Update Error occurred - ' + error);
    }
});


1)在客户端,您可以像下面这样以字符串形式向您发送http.post请求

1
2
var IndexInfo = JSON.stringify(this.scope.IndexTree);
this.$http.post('../../../api/EvaluationProcess/InsertEvaluationProcessInputType',"'" + IndexInfo +"'" ).then((response: any) => {}

2)然后在Web API控制器中,可以对其进行反序列化。

1
2
3
public ApiResponce InsertEvaluationProcessInputType([FromBody]string IndexInfo)
    {
var des = (ApiReceivedListOfObjects<TempDistributedIndex>)Newtonsoft.Json.JsonConvert.DeserializeObject(DecryptedProcessInfo, typeof(ApiReceivedListOfObjects<TempDistributedIndex>));}

3)ApiReceivedListFobjects类应如下所示

1
2
3
4
5
public class ApiReceivedListOfObjects<T>
    {
        public List<T> element { get; set; }

    }

4)确保在步骤2中的jsonconvert.DeserializeObject命令之前,序列化字符串(此处为indexinfo)的结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var resp = @"
    {
       "
"element"": [
        {
           "
"A"":""A Jones"",
           "
"B"":""500015763""
        },
        {
           "
"A"":""B Smith"",
           "
"B"":""504986213""
        },
        {
           "
"A"":""C Brown"",
           "
"B"":""509034361""
        }
        ]
    }"
;

微软举了一个很好的例子:

https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/sending-html-form-data-part-1

首先验证请求

1
if (ModelState.IsValid)

然后使用序列化数据。

1
Content = new StringContent(update.Status)

这里的"status"是复杂类型中的字段。序列化是由.NET完成的,无需担心这一点。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
@model MVCClient.Models.ProductDetails

@{
    ViewBag.Title ="ProductDetails";
}
<script src="~/Scripts/jquery-1.8.2.min.js">
<script type="text/javascript">

    $(document).ready(function () {
        $("#Save").click(function () {
            var ProductDetails = new Object();
            ProductDetails.ProductName =  $("#txt_productName").val();
            ProductDetails.ProductDetail = $("#txt_desc").val();
            ProductDetails.Price= $("#txt_price").val();
            $.ajax({
                url:"http://localhost:24481/api/Product/addProduct",
                type:"Post",
                dataType:'JSON',
                data:ProductDetails,

                success: function (data) {
                    alert('Updated Successfully');
                    //window.location.href="../Index";
                },
                error: function (msg) { alert(msg); }
            });
        });
    });
   
ProductDetails

<form id="form1" method="post">
    <fieldset>
        <legend>ProductDetails</legend>


       
            @Html.LabelFor(model => model.ProductName)
       
       

            <input id="txt_productName" type="text" name="fname">
            @Html.ValidationMessageFor(model => model.ProductName)
       

       
            @Html.LabelFor(model => model.ProductDetail)
       
       

            <input id="txt_desc" type="text" name="fname">
            @Html.ValidationMessageFor(model => model.ProductDetail)
       

       
            @Html.LabelFor(model => model.Price)
       
       

            <input id="txt_price" type="text" name="fname">
            @Html.ValidationMessageFor(model => model.Price)
       



        <p>

            <input id="Save" type="button" value="Create" />
       
</p>
    </fieldset>

</form>
   
        @Html.ActionLink("Back to List","Index")
   

</form>



@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

以下代码返回JSON格式的数据,而不是XML-WebAPI 2:

在global.asax文件中放入以下行

1
2
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

确保您的WebAPI服务需要一个具有与您正在传递的JSON匹配的结构的强类型对象。并确保将要发布的JSON串接起来。

这是我的javascript(使用angguarjs):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$scope.updateUserActivity = function (_objuserActivity) {
        $http
        ({
            method: 'post',
            url: 'your url here',
            headers: { 'Content-Type': 'application/json'},
            data: JSON.stringify(_objuserActivity)
        })
        .then(function (response)
        {
            alert("success");
        })
        .catch(function (response)
        {
            alert("failure");
        })
        .finally(function ()
        {
        });

这是我的WebAPI控制器:

1
2
3
4
5
6
[HttpPost]
[AcceptVerbs("POST")]
public string POSTMe([FromBody]Models.UserActivity _activity)
{
    return"hello";
}