ASP.NET Core v2(2015年)MVC:如何在没有类型的情况下获取绑定到字符串的原始JSON?
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版本的答案。
ASP.NET Core v2 (2015) MVC : 如何获取绑定到字符串而不带类型的原始JSON?
在ASP.NET Core 2中,有一个问题是如何获取绑定到字符串而不带类型的原始JSON。现在我们有了一个解决方案,可以在ASP.NET Core 3.1的Web API中实现。
解决方案如下:
public async TaskPutAsync([FromBody] System.Text.Json.JsonElement entity) { // 在这里写入你的代码 }
这个解决方法同样适用于ASP.NET Core 3.0。
原始问题是很久以前写的,所以我更新了标题,说明这个问题是针对ASP.NET Core 2写的。我认为为.NET Core 3.1提出一个不同的问题,并给出一个新的答案才有意义。
问题: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 TaskReadRequestBodyAsync(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并尝试进行反序列化。
需要注意的是,使用这种方法是否会影响默认的输入格式化设置,以及如何在特定方法中使用此功能,可以通过添加自定义的属性来实现。
问题的原因是在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`的真正限制。
最初我接受了自己的答案,但是由于社区提供了另一个答案,并且获得了更多的票数,所以我取消了这个答案的接受状态。