一切福田,不離方寸,從心而覓,感無不通。

ASP.NET WebApi [FromBody]获取对象值一直为null的问题

解决问题前,首先确定[FormBodyAttribute]的定义以及功能范围,相关资料:

https://docs.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api

其实文中已经讲得足够详细,一般来讲FormUri获取参数不会存在什么疑惑,但在不了解规则的情况下如何设置和获取FormBody标识的值却有些迷惑:我到底该怎么传递参数api才能够获取到参数?很多文章给出的解决方案是利用="json string"这样的方式进行提交,但实在太别扭了。这样的代码写出去会被打吧…所以,到底该怎么请求呢?

如果你仔细阅读过文章,相信你应该注意到了这一段:

When a parameter has [FromBody], Web API uses the Content-Type header to select a formatter. 

意思是对于被标记为FromBody的parameter,WebApi默认会根据Content-Type中选择格式化方法。由于Web程序中常常使用JSON方式传递数据,所以这里只针对Content-Type="application/json"的请求进行分析。接着看下一句:

In this example, the content type is "application/json" and the request body is a raw JSON string (not a JSON object).

其实到这里已经给出解决方案了,即Content-Type="application/json"的请求都需要将参数转换为JSON string,而非JSON Object.

接下来代码说话的时间到了:

1)创建一个复杂对象的实体类DataParameter:

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
[Serializable]
public class DataParameter
{
    public DataParameter()
    {
        this.DataItems = new List<DataItemParameter>();
    }
    public int Id { getset; }
    public List<DataItemParameter> DataItems { getset; }
}
[Serializable]
public class DataItemParameter
{
    public DataItemParameter() { }
    public DataItemParameter(int id, string name)
    {
        this.Id = id;
        this.Name = name;
    }
    [Range(10, 20)]
    public int Id { getset; }
    [Required]
    public string Name { getset; }
    public List<DataItemProperty> Properties { getset; }
}
[Serializable]
public class DataItemProperty
{
    public string Name { getset; }
}

2)创建一个ApiController的派生类,并创建两个接收数据并返回的Action:

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
[RoutePrefix("api/data")]
    public class DataController : ApiController
    {
        /// <summary>
        /// 复杂数据类型
        /// </summary>
        /// <param name="parameter">Complex Data</param>
        /// <returns></returns>
        [Route("complex"), HttpPost]
        public DataParameter PostComplexData([FromBody]DataParameter parameter)
        {
            return parameter;
        }
        /// <summary>
        /// 简单数据类型请求
        /// <![CDATA[
        /// primitive types:[int, bool, double, and so forth]
        /// plus types:[TimeSpan, DateTime, Guid, decimal, and string]
        /// ]]>
        /// </summary>
        /// <param name="parameter">Simple Data</param>
        /// <returns></returns>
        [Route("simple"), HttpPost]
        public string PostSimpleData([FromBody] string parameter)
        {
            return parameter;
        }

3)创建请求(Postman| jQuery):

3.1)Postman:设置请求方式为POST,填写请求地址:http://localhost:2364/api/data/complex(本机api请求地址)

设置

1
Content-Type="application/json"

Body选择Raw,填入请求数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
    "Id": 1,
    "DataItems": [{
        "Id": 1,
        "Name""DI1",
        "Properties": [{
            "Name""DI1->P1"
        }]
    }, {
        "Id": 2,
        "Name""DI2",
        "Properties": [{
            "Name""DI2->P2"
        }]
    }]
}

点击请求,发现已经可以接收到数据并返回:

查看发送内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
POST /api/data/complex HTTP/1.1
Host: localhost:2364
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: f1bc12c3-3694-4113-b4af-7d4cac6c60f2
{
    "Id": 1,
    "DataItems": [{
        "Id": 1,
        "Name""DI1",
        "Properties": [{
            "Name""DI1->P1"
        }]
    }, {
        "Id": 2,
        "Name""DI2",
        "Properties": [{
            "Name""DI2->P2"
        }]
    }]
}

这里可以看到发送了一个完整的JSON对象,同时服务器也将这个对象返回给了我们。

3.2)JQuery

啰嗦一段:考虑一个问题,当我们使用jQuery.post、jQuery.ajax发送请求时,附带的data是一个对象还是一个字符串呢?

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
$.ajax({<br>  url: "/api/data/complex",
   type: "POST",
   contentType: "application/json",
   data: JSON.stringify({<br>          "Id": 1,
               "DataItems": [{
                 "Id": 1,
                 "Name""DI1",
                 "Properties": [{
                   "Name""DI1->P1"
                  }]
                }, <br>          {<br>            "Id": 2,
                    "Name""DI2",
                    "Properties": [{
                      "Name""DI2->P2"
                    }]
                 }]
               }),
               success: function (res) {
                  console.log(res);
               }
            });
            //简易数据类型
            $.ajax({
                url: "/api/data/simple",
                type: "POST",
                contentType: "application/json",
                data: JSON.stringify("请求类型为application/json,内容为json字符串"),
                success: function (res) {
                    console.log(res);
                }
            });

请注意jQuery发送请求时data并非一个JSON对象,而是一个被JSON.stringify(data)后的JSON字符串。为什么会这样?打开浏览器控制台看看它们的差别吧 :)

最后做一个总结吧:

1、请求时Content-Type统一设为application/json,可以避免Content-Type不一致导致的问题,方便维护和查找问题。

2、在Postman中请求时除了设置Content-Type外,不要忘了在Body中选择Raw方式。

3、ajax请求时,data不是一个JSON Object,而是一个JSON字符串。

 

from:http://www.cnblogs.com/lin-du/p/8709212.html