ASP.NET Core v2(2015年)MVC:如何在没有类型的情况下获取绑定到字符串的原始JSON?

19 浏览
0 Comments

ASP.NET Core v2(2015年)MVC:如何在没有类型的情况下获取绑定到字符串的原始JSON?

类似于这个关于之前ASP.NET版本的旧问题,我想要获取HTTP POST请求的请求体并将其绑定为一个字符串。当ASP.NET调用我的控制器方法时,似乎该方法已经进行了绑定,但是`value`却是空的:

namespace Demo.Controllers
{
    [Route("[controller]")]
    public class WebApiDemoController : Controller
    {
    ...
    // POST api/values
    [HttpPost]
    public System.Net.Http.HttpResponseMessage Post([FromBody]string value)
    {
       // 期望:value = JSON字符串,实际:value = null。
    }
}

我是否仍然需要从流中获取请求体?或者这个方法应该直接工作?在测试上述方法时,我使用了以下的HTTP头:

Accept: Application/json

Content-Type: Application/json;charset=UTF-8

我在请求体中传入了以下内容:`{ "a": 1 }`

我不想将其绑定到名为`a`的字符串变量上。我想要绑定任何我得到的JSON,然后我想在我的方法中使用JSON内容,任何任意的内容。

如果我理解文档正确的话,`[FromBody]`属性应该做到我想要的效果,但我猜测ASP.NET Core MVC的绑定机制不会将JSON绑定到一个"string value",但也许我可以做一些其他的事情来达到相同的灵活性。

一个类似的问题在这里给了我一个想法,也许我应该改为使用`[FromBody] dynamic data`,而不是使用`[FromBody] string value`。

更新:这里有针对.NET Core 6和其他现代.NET Core版本的答案。

0
0 Comments

ASP.NET Core v2 (2015) MVC : 如何获取绑定到字符串而不带类型的原始JSON?

在ASP.NET Core 2中,有一个问题是如何获取绑定到字符串而不带类型的原始JSON。现在我们有了一个解决方案,可以在ASP.NET Core 3.1的Web API中实现。

解决方案如下:

public async Task PutAsync([FromBody] System.Text.Json.JsonElement entity)
{ 
    // 在这里写入你的代码
}

这个解决方法同样适用于ASP.NET Core 3.0。

原始问题是很久以前写的,所以我更新了标题,说明这个问题是针对ASP.NET Core 2写的。我认为为.NET Core 3.1提出一个不同的问题,并给出一个新的答案才有意义。

0
0 Comments

问题:ASP.NET Core v2(2015)MVC:如何获取绑定到字符串的原始JSON而不需要类型?

原因:在ASP.NET Core v2 MVC中,默认情况下,将JSON请求体绑定到复杂类型。但是有时我们需要将请求体直接绑定到字符串,以便获取原始的JSON数据。

解决方法:可以自定义一个简单的InputFormatter来实现这个功能。首先,创建一个名为RawJsonBodyInputFormatter的类,继承自InputFormatter。在该类中,重写ReadRequestBodyAsync方法,读取请求体的内容并返回原始的JSON字符串。同时,重写CanReadType方法,指定只能读取字符串类型。然后,在Startup.cs文件的ConfigureServices方法中,将该自定义的InputFormatter添加到MvcOptions中的InputFormatters集合中。这样就可以在控制器中获取原始的JSON数据。

具体代码如下:

public class RawJsonBodyInputFormatter : InputFormatter
{
    public RawJsonBodyInputFormatter()
    {
        this.SupportedMediaTypes.Add("application/json");
    }
    
    public override async Task ReadRequestBodyAsync(InputFormatterContext context)
    {
        var request = context.HttpContext.Request;
        using (var reader = new StreamReader(request.Body))
        {
            var content = await reader.ReadToEndAsync();
            return await InputFormatterResult.SuccessAsync(content);
        }
    }
    
    protected override bool CanReadType(Type type)
    {
        return type == typeof(string);
    }
}
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc(options =>
        {
            options.InputFormatters.Insert(0, new RawJsonBodyInputFormatter());
        });
    }
}

使用这种方法,就可以在控制器的方法中直接获取原始的JSON数据,如下所示:

[HttpPost]
public IActionResult Post([FromBody] string value)
{
    // value will be the request json payload
}

这样做非常简洁和高效。如果请求的内容类型是"text/plain",也可以通过在RawJsonBodyInputFormatter的构造函数中添加`this.SupportedMediaTypes.Add("text/plain");`来支持解析纯文本。

另外,如果请求的内容类型和API接受的内容类型都是"text/plain",那么可以直接获取字符串,不需要进行额外的处理,因为ASP.NET Core不会将纯文本内容视为JSON并尝试进行反序列化。

需要注意的是,使用这种方法是否会影响默认的输入格式化设置,以及如何在特定方法中使用此功能,可以通过添加自定义的属性来实现。

0
0 Comments

问题的原因是在ASP.NET Core 2.x中,无法直接将原始JSON绑定到字符串类型。解决方法是使用`dynamic`作为参数列表的`[FromBody]dynamic data`,而不是使用`string`,这样就可以接收到一个`JObject`对象。

值得注意的是,如果你的架构要求一个单一的WebApi服务器能够流畅地生成XML和JSON,取决于内容类型头条目,那么这种直接使用JSON的策略可能会适得其反。(通过足够的工作来支持在同一个服务上同时使用XML和JSON是可能的,但这样你就把更上层的MVC资产管道中的东西移到了控制器方法中,这与MVC的精神相悖,因为模型已经以POCO的形式被解析了。)

一旦在方法内部将`JObject`转换为字符串,就可以将传入的`JObject`(Newtonsoft.JSON中用于表示JSON的内存数据类型)转换为字符串。

在Microsoft.AspNetCore.Mvc.Core 2.0.0中,以上方法不起作用,我可以得到一个模型,但似乎无法获取属性,例如`[FromBody] dynamic data => data.[someproperty]`,会抛出属性未找到的异常。

如果你找到了解决方法,请随时编辑我的答案并添加相应的.NET Core 2代码。也许你只需要添加一些命名空间以引入一些辅助工具,或者`dynamic`的设计发生了改变。

晚来了,但我只是想让大家知道,使用`JObject`方法在2.2版本中是可行的。

对于.NET Core 3.0+,你需要显式引用`Microsoft.AspNetCore.Mvc.NewtonsoftJson`包,并在启动文件中激活它:`services.AddMvc().AddNewtonsoftJson()`。

你是否找到了一种在`System.Text.Json`中将JSON绑定到`System.Text.Json`类之一的等效方法,而不使用Newtonsoft?昨天我花了几个小时试图将JSON绑定到`System.Text.Json`类,但没有成功。这是我遇到的第一个`System.Text.Json`的真正限制。

最初我接受了自己的答案,但是由于社区提供了另一个答案,并且获得了更多的票数,所以我取消了这个答案的接受状态。

0