FromBody字符串参数返回空值。

91

这可能是非常基础的问题,但我在弄清楚自己做错了什么方面遇到了困难。

我正在尝试从POST请求的正文中获取一个字符串,但是 "jsonString" 只显示为空。我也想避免使用模型,但也许这是不可能的。我在PostMan中使用的代码片段如下:

[Route("Edit/Test")]
[HttpPost]
public void Test(int id, [FromBody] string jsonString)
{
    ...
}
也许我在Postman方面做错了什么,但我一直试图在请求体的x-www-form-urlencoded部分的值区域中使用“=test”(如其他关于此主题的问题所见),并将键设置为jsonString且值为空。我还尝试过使用raw-text和raw-text/plain。我得到了id,所以我知道url是正确的。非常感谢您对此的任何帮助。
当前PostMan的设置如下:
POST http://localhost:8000/Edit/Test?id=111
key = id  value = 111
Body - x-www-form-urlencoded
key = jsonString  value = "=test"

请在您的问题中提供完整的HTTP请求,包括URL和请求体。 - Matt Hensley
至少 Request.Content.ReadAsStringAsync() 应该可以工作。 - Fabio
3
我相信这是可能的。请设置您的请求头 Content-Type: application/x-www-form-urlencoded,并将请求体设置为 =test(没有其他内容)。 - Igor
Asp.Net Core 相关问题 https://dev59.com/KlwZ5IYBdhLWcg3wBcQJ - Michael Freidgeim
你可以通过访问以下链接找到我的答案:https://stackoverflow.com/questions/41559050/string-value-is-empty-when-using-frombody-in-asp-net-web-api/69975991#69975991 - CagriK
显示剩余2条评论
17个回答

89

通过使用[FromBody]声明jsonString参数,您告诉ASP.NET Core使用输入格式化程序将提供的JSON(或XML)绑定到模型。因此,如果您提供了一个简单的模型类,那么您的测试应该可以工作。

public class MyModel
{
    public string Key {get; set;}
}

[Route("Edit/Test")]
[HttpPost]
public void Test(int id, [FromBody] MyModel model)
{
    ... model.Key....
}

以及发送的JSON数据如下:

{
    key: "value"
}

当然,您可以跳过模型绑定,通过在控制器中访问 HttpContext.Request 直接检索提供的数据。 HttpContext.Request.Body 属性为您提供内容流,或者您可以通过 HttpContext.Request.Forms 访问表单数据。

个人而言,我更喜欢模型绑定,因为它具有类型安全性。


2
@Fabio - 如果使用 [FromBody],那么它将无法正常工作,因为它告诉框架你想要将数据绑定到一个模型类。为了避免绑定,跳过此参数并按照最后一段中的提示直接访问发送的数据即可。希望这能帮到你。 - Ralf Bönning
这对我有用 - 你只需要在POSTMan的POST选项中使用RAW而不是FORM。 - Codeman
运行得非常顺利,谢谢。不知道为什么我们不能直接从正文传递一个字符串,但是...呃...嘿嘿,谢谢。 - jPhizzle
3
如果只是一个字符串,那么你不需要创建一个模型,而只需要传递一个JSON字符串,该字符串需用双引号括起来,例如:"test" 而不是只有 test。 - Etherman
所以这不适用于简单的变量类型,比如字符串。 需要注意的一点是,[FromBody] 无法与异步 jQuery/javascript(客户端)一起使用 - 即使您提供了模型,它也始终为空。在 POST/PUT 调用中,data: 将被完全忽略。 - MC9000

65

参考ASP.NET Web API中的参数绑定

使用[FromBody]

为了强制Web API从请求体中读取简单类型,请将[FromBody]属性添加到参数中:

[Route("Edit/Test")]
[HttpPost]
public IHttpActionResult Test(int id, [FromBody] string jsonString) { ... }
在这个例子中,Web API将使用媒体类型格式化器从请求正文中读取jsonString的值。这是一个客户端请求的示例。

在这个例子中,Web API将使用媒体类型格式化器从请求正文中读取jsonString的值。这是一个客户端请求的示例。

POST http://localhost:8000/Edit/Test?id=111 HTTP/1.1
User-Agent: Fiddler
Host: localhost:8000
Content-Type: application/json
Content-Length: 6

"test"
当参数带有 [FromBody] 时,Web API 使用 Content-Type 头来选择格式化程序。在此示例中,内容类型为“application/json”,请求体是原始的 JSON 字符串(而不是 JSON 对象)。
在上面的示例中,如果数据以正确的格式在主体中提供,则不需要模型。
对于 URL 编码,请求将如下所示。
POST http://localhost:8000/Edit/Test?id=111 HTTP/1.1
User-Agent: Fiddler
Host: localhost:8000
Content-Type: application/x-www-form-urlencoded
Content-Length: 5

=test

有没有任何方法可以使用 multipart/form-data 实现这个?据我所知,这种方式不允许无键入的条目。 - Rudey

47

有点取巧,但非常有效。如果不需要丰富的对象,则是更好的解决方案。 - Paw Baltzersen
如何使用此方法发送两个参数?而不使用某个 Model 类的对象。 - Junaid Pathan
@JunaidPathan,一般来说最好提出一个新问题并提供更多的上下文。您只能有一个FromBody参数,但可以有多个fromQuery。 - Michael Freidgeim
只需像处理对象一样使用 JSON.stringify(myString) 即可。 - Desperado
@Desperado,这是一个不错的建议,适用于JavaScript客户端,但也可以使用其他工具,包括curl和postman。 - Michael Freidgeim

12

我知道这个答案有点旧了,而且已经有一些非常好的回答解决了这个问题。为了进一步扩展这个问题,我想提一件让我疯狂了4或5个小时的事情。

在您的模型类中,非常重要的一点是确保您的属性启用了set属性。

以下内容将不会起作用(参数仍为空):

/* Action code */
[HttpPost]
public Weird NOURLAuthenticate([FromBody] Weird form) {
    return form;
}
/* Model class code */
public class Weird {
    public string UserId {get;}
    public string UserPwd {get;}
}

这个一定会成功:

/* Action code */
[HttpPost]
public Weird NOURLAuthenticate([FromBody] Weird form) {
    return form;
}
/* Model class code */
public class Weird {
    public string UserId {get; set;}
    public string UserPwd {get; set;}
}

1
谢谢!即使省略get;和set;也不起作用(空参数)。 - Andrew

11

将原始的JSON字符串发布,并不要忘记双引号!

输入图像描述


11

在我的情况下,我忘记使用了

JSON.stringify(bodyStuff).

谢谢!对于使用 Axios 的任何人来说,这可能是你正在寻找的解决方案。Axios 以非常特定的方式处理请求体字符串,即使你像 "test" 这样硬编码字面量,请求体也会反映出 test ... 这不是一个有效的 JSON 字面量。感谢 Eugene 在这里节省了我数小时的时间! - spencer741

10

你正在正确的道路上。

在你的标题中设置

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

POST请求的主体应该是=test,不要有其他内容。对于未知/可变字符串,您需要对其进行URL编码,以便不会意外转义输入字符。


另请参阅将字符串POST到ASP.NET Web Api应用程序-返回null


5

经过1小时的努力,终于让它工作了。

这将解决空值问题,并以通用方式(无模型绑定)获取JSON key1value1值。

对于一个新的WebApi 2应用程序示例:

Postman(看起来就像下面这样):

POST    http://localhost:61402/api/values   [Send]
Body
     (*) raw             JSON (application/json) v
         "{  \"key1\": \"value1\" }"

上面的端口号61402或链接/api/values可能与您的不同。

ValuesController.cs

using Newtonsoft.Json;
// ..

// POST api/values
[HttpPost]
public object Post([FromBody]string jsonString)
{
    // add reference to Newtonsoft.Json
    //  using Newtonsoft.Json;

    // jsonString to myJsonObj
    var myJsonObj = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(jsonString);

    // value1 is myJsonObj[key1]
    var valueOfkey1 = myJsonObj["key1"];

    return myJsonObj;
}

目前一切顺利,如果我有子键,是否需要将模型绑定到类中?或者,对于子键,可能使用DeserializeObject会更好。


5

如果您不想/不需要被绑定到具体的类,可以直接将JSON传递给WebAPI控制器。该控制器可以使用ExpandoObject类型来接受JSON。以下是示例方法:

public void Post([FromBody]ExpandoObject json)
{
    var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)json);
}

Content-Type头设置为application/json并将JSON作为正文发送。 keyValuePairs对象将包含JSON键/值对。

或者您可以使方法接受输入的JSON作为JObject类型(从Newtonsoft JSON库中),并通过将其设置为dynamic类型,您可以通过点符号访问属性。

public void Post([FromBody]JObject _json)
{
    dynamic json = _json;
}

3
整个白天我都在解决类似的问题。
你必须知道内置序列化器和Newtonsoft的工作方式不同。 在我的情况下,内置的无法将JSON数字解析为System.String。 但是我没有明显的异常或详细信息,只是数据为空。
我仅在记录了ModelState时才发现了这一点:
logger.LogInformation($"ModelState = {ModelState.IsValid}");
string messages = string.Join("; ", ModelState.Values
                    .SelectMany(x => x.Errors)
                    .Select(x => x.ErrorMessage));
logger.LogInformation($"ModelMessages = {messages}");

然后我在日志中看到了特定的异常:

The JSON value could not be converted to System.String

我做的修复方法如下:

  1. 安装预览版的 Microsoft.AspNetCore.Mvc.NewtonsoftJson
  2. 更改为 services.AddControllers().AddNewtonsoftJson();

解决方案来自https://dev59.com/n1MH5IYBdhLWcg3w6VUv#57652537


正在从ASP.NET Core 2.x进行更新时,遇到了使用Newtonsoft类似的问题。你节省了我很多时间! - didge
@didge 很高兴我能帮到你。 :) - Anton Semenov
1
这是非常有用的信息,也解决了我的问题。 - zameb

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