将 JSON post 模型解包到动作参数
Unpacking a JSON post model to action parameters
我的任务是将大型框架 ASP.NET MVC 应用程序升级到 .NET 5。大部分工作已完成,但我 运行 遇到 HTTP 问题 POST 从前端调用 JSON 个对象。
这些 POST 调用中的大多数都有这样的 JSON 正文:
{
"param1" : "hello",
"param2" : "world",
"param3" : 123
}
其对应的控制器动作如下所示:
public ActionResult SaveData(string param1, string param2, int param3)
{
//Do save stuff
}
这在 ASP.NET Core 中不起作用。参数保持为空。我确实找到了一个解决方案,就是将这些参数封装在一个模型对象中,并将其作为唯一具有 [FromBody] 属性的参数。但是,此应用程序有数百个具有各种参数的操作,我真的不希望必须为所有这些编写模型。
所以,我正在寻找一种方法来告诉 ASP.NET 将这些 JSON 对象的属性视为相应操作的参数。我用谷歌搜索了一下,但找不到任何有用的东西。有什么办法可以做到这一点,还是我必须为每个动作编写模型?
我的一位同事发挥了他的 Google-fu 并最终找到了 this library,这正是我所需要的。
您可以为此创建自定义 ValueProvider
。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Mime;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
public class JsonValueProviderFactory : IValueProviderFactory
{
public async Task CreateValueProviderAsync(ValueProviderFactoryContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
string contentType = context.ActionContext.HttpContext.Request.Headers[HeaderNames.ContentType].FirstOrDefault();
bool isJson = contentType == null
? false
: contentType.StartsWith(MediaTypeNames.Application.Json, StringComparison.OrdinalIgnoreCase);
if (isJson)
{
context.ActionContext.HttpContext.Request.EnableBuffering();
using (var reader = new StreamReader(context.ActionContext.HttpContext.Request.Body, Encoding.UTF8, false, 1024, true))
{
string body = await reader.ReadToEndAsync();
var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(body);
context.ActionContext.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin); // rewind
var valueProvider = new JsonValueProvider(values);
context.ValueProviders.Add(valueProvider);
}
}
}
}
//todo: implement better logic for nested objects
public class JsonValueProvider : IValueProvider
{
private Dictionary<string, object> _values;
public JsonValueProvider(Dictionary<string, object> values)
{
_values = new Dictionary<string, object>(values, StringComparer.OrdinalIgnoreCase);
}
public bool ContainsPrefix(string prefix) => _values.ContainsKey(prefix);
public ValueProviderResult GetValue(string key)
{
return _values.TryGetValue(key, out object value)
? new ValueProviderResult(Convert.ToString(value))
: ValueProviderResult.None;
}
}
在Startup.cs
中注册JsonValueProviderFactory
services
.AddMvc(options =>
{
options.ValueProviderFactories.Add(new JsonValueProviderFactory());
//...
});
备注
此实现仅支持普通值而不支持复杂对象,需要进行一些测试。如果您将复杂对象绑定为操作参数,请 not 忘记指定 FromBody
属性,以便发生默认模型绑定并且此值提供程序不会破坏任何内容。
我的任务是将大型框架 ASP.NET MVC 应用程序升级到 .NET 5。大部分工作已完成,但我 运行 遇到 HTTP 问题 POST 从前端调用 JSON 个对象。
这些 POST 调用中的大多数都有这样的 JSON 正文:
{
"param1" : "hello",
"param2" : "world",
"param3" : 123
}
其对应的控制器动作如下所示:
public ActionResult SaveData(string param1, string param2, int param3)
{
//Do save stuff
}
这在 ASP.NET Core 中不起作用。参数保持为空。我确实找到了一个解决方案,就是将这些参数封装在一个模型对象中,并将其作为唯一具有 [FromBody] 属性的参数。但是,此应用程序有数百个具有各种参数的操作,我真的不希望必须为所有这些编写模型。
所以,我正在寻找一种方法来告诉 ASP.NET 将这些 JSON 对象的属性视为相应操作的参数。我用谷歌搜索了一下,但找不到任何有用的东西。有什么办法可以做到这一点,还是我必须为每个动作编写模型?
我的一位同事发挥了他的 Google-fu 并最终找到了 this library,这正是我所需要的。
您可以为此创建自定义 ValueProvider
。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Mime;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
public class JsonValueProviderFactory : IValueProviderFactory
{
public async Task CreateValueProviderAsync(ValueProviderFactoryContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
string contentType = context.ActionContext.HttpContext.Request.Headers[HeaderNames.ContentType].FirstOrDefault();
bool isJson = contentType == null
? false
: contentType.StartsWith(MediaTypeNames.Application.Json, StringComparison.OrdinalIgnoreCase);
if (isJson)
{
context.ActionContext.HttpContext.Request.EnableBuffering();
using (var reader = new StreamReader(context.ActionContext.HttpContext.Request.Body, Encoding.UTF8, false, 1024, true))
{
string body = await reader.ReadToEndAsync();
var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(body);
context.ActionContext.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin); // rewind
var valueProvider = new JsonValueProvider(values);
context.ValueProviders.Add(valueProvider);
}
}
}
}
//todo: implement better logic for nested objects
public class JsonValueProvider : IValueProvider
{
private Dictionary<string, object> _values;
public JsonValueProvider(Dictionary<string, object> values)
{
_values = new Dictionary<string, object>(values, StringComparer.OrdinalIgnoreCase);
}
public bool ContainsPrefix(string prefix) => _values.ContainsKey(prefix);
public ValueProviderResult GetValue(string key)
{
return _values.TryGetValue(key, out object value)
? new ValueProviderResult(Convert.ToString(value))
: ValueProviderResult.None;
}
}
在Startup.cs
JsonValueProviderFactory
services
.AddMvc(options =>
{
options.ValueProviderFactories.Add(new JsonValueProviderFactory());
//...
});
备注
此实现仅支持普通值而不支持复杂对象,需要进行一些测试。如果您将复杂对象绑定为操作参数,请 not 忘记指定 FromBody
属性,以便发生默认模型绑定并且此值提供程序不会破坏任何内容。