POST参数始终为空

224

自从升级到WebAPI的RC版本后,我在调用WebAPI的POST方法时遇到了一些奇怪的问题。我甚至回到了新项目生成的基本版本。所以:

public void Post(string value)
{
}

并且从 Fiddler 调用:

Header:
User-Agent: Fiddler
Host: localhost:60725
Content-Type: application/json
Content-Length: 29

Body:
{
    "value": "test"
}

当我调试时,字符串"value"从未被赋值。它始终为NULL。 有人遇到过这个问题吗?

(我最初是在更复杂的类型中遇到了这个问题)

这个问题不仅限于ASP.NET MVC 4,对于一个安装了RC版本的全新ASP.NET MVC 3项目也存在相同的问题。


只是为了增加问题的难度 - 这不仅限于 JSON,也会发生在 XML 中。 - ianrathbone
3
我已经为此奋斗了两天,在阅读了我能找到的所有文章后,结果证明问题非常简单,只需要正确格式化 WebRequest 中的 JSON 字符串:数据必须以双引号开头和结尾,如果您在 json 数据中使用单引号,那么一切都很顺利。 - Gineer
43个回答

123

今天我在思考这个问题。我的解决方案是将[FromBody]更改为HttpRequestMessage,从而将HTTP堆栈上移。

在我的情况下,我正在通过网络发送压缩的JSON数据,然后对其进行base64编码。所有这些都来自Android应用程序。

我的Web端点的原始签名如下(使用[FromBody]):

My original endpoint

我针对这个问题的解决方法是还原到使用HttpRequestMessage作为我的端点签名。

enter image description here

然后,您可以使用以下代码行访问发布数据:

enter image description here

这有效,并允许您访问未经处理的原始发布数据。您无需使用Fiddler在字符串开头放置=符号或更改内容类型。

顺便说一句,我首先尝试了上面的其中一个答案,即将内容类型更改为:"Content-Type:application/x-www-form-urlencoded"。对于原始数据,这是个坏建议,因为它会剥离+字符。

因此,以这种方式开头的base64字符串:“MQ0AAB + LCAAAAAA”会变成“MQ0AAB LCAAAAAA”! 这不是你想要的。

使用HttpRequestMessage的另一个好处是您可以从端点内部访问所有HTTP标头。


7
优秀的解决方案……我花了9个小时才找到它,将[FromBody]字符串值移除并替换为HttpRequestMessage后,我的问题得以解决。 - Kev
1
运行得非常好!但是有没有一种方法可以使用特定对象的实际参数类型?例如,Web API 方法的 Order 类型参数? - Ron
10
顺便提一下,在方法签名中你不需要 HttpRequestMessage request,因为你已经有了它。在方法体中,它可以通过 Request 对象进行访问。例如:Request.Content.ReadAsStringAsync().Result; - Morvael
通常这个问题是由于你的JSON对象不正确导致的。我发现这通常是因为构造函数中有一个无效类型,而Json不知道如何将其转换,例如Guid到字符串。所以要么通过设置添加转换器,要么添加一个空构造函数,这样就不需要回到旧代码了。 - Nick Turner
最简单的方法是将对象转换为字符串,然后尝试进行转换,您会看到JSON错误。同时请检查您的标头。 - Nick Turner

111

10
我尝试使用[FromBody],但它没有任何改变。 - ianrathbone
12
只传递值(不作为JSON对象)应该可以正常工作,参考http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx。 - Jim Harte
8
在我的经验中,使用=作为开头的附加内容从未起到过作用,直到我按照Jim在他的评论中给出的建议(不作为JSON对象)进行操作,才得以成功。这一点非常关键!我真的不喜欢WebAPI的挑剔程度。 - gitsitgo
24
这太愚蠢和烦人了,我不知道是给那个帮助我解决问题的回答点赞还是点踩... 为什么非得用这种格式呢?(抱歉态度有些不好,浪费了太多时间,但这一点也没有意义... :()你真的应该增加对人们期望的格式的支持。 - gdoron
8
谢谢!我们到底怎么知道只发送一个参数时要删除参数名称?是哪个脑袋不灵光的人想出了这个主意? - Krisztián Balla
显示剩余6条评论

74

我刚使用 Fiddler 时也遇到了这个问题。问题的原因是我没有指定 Content-Type

在你的 POST 请求中加入一个 Content-Type 的头部信息试试。

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

或者,根据下方的评论,您可能需要包含一个JSON头

Content-Type: application/json

11
我有一个类似的问题,只不过我需要设置Content-Type为application/json。 - dvallejo
2
如帖子中所指定的 - 我已经添加了 Content-Type: application/json。 - ianrathbone
2
application/x-www-form-urlencoded 对我不起作用,Content-Type: application/json 可以。 - liang
2
无论内容类型如何,如果您只有一个参数,则必须仅在请求正文中发送值,而不是参数名称。因此,id=13是行不通的。您需要单独发送13。请参见吉姆的回答。 - Krisztián Balla
我不得不使用 contentType: "application/x-www-form-urlencoded; charset=UTF-8",完整示例请参见完整的客户端和服务器 - RyBolt

62
我也遇到了这个问题,以下是我解决问题的方法。
webapi 代码:
public void Post([FromBody] dynamic data)
{
    string value = data.value;
    /* do stuff */
}

客户端代码:

$.post( "webapi/address", { value: "some value" } );

1
如果客户端发送的是JSON,则此方法有效。如果我发送一个简单的值,例如字符串“some value”,那么“data”将为空。 - brianestey
简单易行,只需更改您的客户端代码以发送一个JS对象即可。 - George
从JSON获取数据的好而干净的方法 - Wouter Vanherck
如果您正在发送JSON,则可以使用以下代码将数据序列化为JSON格式:var json = JsonConvert.SerializeObject(data); - dvallejo
太棒了。你甚至可以像这样获取传入的数据。string value = _data.value; - undefined

47

我正在使用 Postman 工具,但我犯了同样的错误... 将 value 作为 JSON 对象传递而不是字符串。

{
    "value": "test"
}

很明显,当api参数为字符串类型时,上述内容是错误的

因此,在api主体中只需将字符串用双引号括起来即可:

"test"

2
这是我的问题,并且很可能是问题的真正解决方案。JSON编码的字符串需要引号。 - Kyle W
1
我在Postman上也遇到了这个问题。原来当Content-Type设置为application/json时,我错误地选择了“form-data”而不是“raw”。您可以参考zzyykk123456在aspnet Github的Issues中的截图:https://github.com/aspnet/Home/issues/2202 - Chun Lin
这对我很有效。使用Swagger(swashbuckle),我必须设置Content Type: application/json并使用双引号。 - John Henckel
1
正是我的问题,但我必须指出,在Ajax请求中,您应该执行'data: JSON.stringify("YourString")'。 - aradalvand
1
@AmirHosseinAhmadi 我刚在 AJAX 中遇到了这个问题,发现使用 JSON.stringify 导致我的 [frombody] 为空。将数据字段设置为字符串值(即 JSON 字符串)后它可以正常工作。 - Nexaspx

19
尝试创建一个类作为数据模型,然后发送一个JSON对象,其属性与数据模型类的属性匹配。(注意:我已经测试过这个方法,并且在我今天刚下载的最新MVC 4 RC 2012中可用。)
public HttpResponseMessage Post(ValueModel model)
{
    return Request.CreateResponse<string>(HttpStatusCode.OK, "Value Recieved: " + model.Value);
}

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

以下 JSON 对象通过 HTTP POST 请求发送,内容类型为 application/json

{ "value": "In MVC4 Beta you could map to simple types like string, but testing with RC 2012 I have only been able to map to DataModels and only JSON (application/json) and url-encoded (application/x-www-form-urlencoded body formats have worked. XML is not working for some reason" }
我认为你需要创建一个数据模型类的原因是因为简单的值被认为来自URL参数,而单个复杂的值被认为来自请求体。虽然它们有[FromBody][FromUrl]属性,但使用[FromBody] string value对我来说仍不起作用。似乎他们仍在解决许多错误,所以我相信这将来会改变。
编辑: 在请求体中成功使用XML。默认的XML序列化程序已更改为DataContractSerializer而不是XmlSerializer。将以下行放入Global.asax文件中即可解决此问题(参考链接)。
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;

这个对我有用的代码是:request.ContentType = "application/json; charset=UTF-8"; - Arvind Krmar

17

经过几次尝试,我认为默认行为是正确的,没有什么需要修改的。

唯一的技巧是:如果你的post方法参数是像下面这样的字符串,则应该在请求主体中发送一个带有双引号的纯字符串(在使用ajax或postman时),例如:

//send "{\"a\":1}" in body to me, note the outer double quotes
[HttpPost("api1")]
public String PostMethod1([FromBody]string value)
{
    return "received " + value; //  "received {\"a\":1}"
}
否则,如果您在POST请求主体中发送一个没有外部双引号和转义内部引号的JSON字符串,则应该能够解析为模型类(参数类型),例如:{"a":1, "b":2}
public class MyPoco{
    public int a;
    public int b;
}

//send {"a":1, "b":2} in body to me
[HttpPost("api2")]
public String PostMethod2([FromBody]MyPoco value)
{
    return "received " + value.ToString();  //"received your_namespace+MyPoco"
}

14

我已经寻找解决这个问题的方案有几分钟了,所以我将分享我的解决方案。

如果您发布一个模型,您的模型需要有一个空/默认构造函数,否则模型无法被创建,显然。 在重构时要小心。;)


重构正是我在这里遇到的问题,感谢您的提示! - Alan

11

如果您在使用Swagger或Postman时遇到与我类似的问题,即使已经指定了“ContentType”,如果您正在发送一个简单的字符串属性作为post请求,仍然会收到null值。

只传递:

MyValue

传递到控制器中将变成null。

但是,如果您传递:

"MyValue"

那么该值将会被正确接收。

引号会有所不同。当然,这仅适用于Swagger和Postman。例如,在使用Angular的前端应用程序中,框架应该会自动解决此问题。


如果你在传递 JSON 数据时包含引号,可以用单引号将整个字符串括起来。 - daveywc

10

这对我有用:

  1. 创建一个C# DTO类,其中包含从jQuery/Ajax传递的每个属性的属性。

public class EntityData
{
    public string Attr1 { get; set; }
    public string Attr2 { get; set; }
}
  • 定义Web API方法:

  • [HttpPost()]
    public JObject AddNewEntity([FromBody] EntityData entityData)
    {
    
  • 按照以下方式调用Web API:

    var entityData = {
        "attr1": "value1",
        "attr2": "value2"
    };
    
    $.ajax({
        type: "POST",
        url: "/api/YOURCONTROLLER/addnewentity",
        async: true,
        cache: false,
        data: JSON.stringify(entityData),
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (response) {
            ...
        }
    });
    

  • 谢谢您发布这篇文章,我尝试了很多示例,但这个对我有用! - Satbir

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接