[英]Binding .NET Core Json to simple parameters
我过去只是将简单的 json 对象发布到 ASP.NET MVC 控制器,绑定引擎会将主体解析为方法的简单参数:
{
"firstname" : "foo",
"lastname" : "bar"
}
我可以有这样的 MVC controller:
public method Blah(string firstname, string lastname) {}
firstname
和lastname
会自动从 Json object 中提取并映射到简单参数。
I've moved a backend piece to .NET Core 5.0 with the same method signatures, however, when I post the same simple JSON object, my parameters are null. 我什至尝试对每个参数执行[FromBody]
,但它们仍然是 null。 直到我创建了一个包含参数名称的额外 class 才会 model 绑定工作:
public class BlahRequest
{
public string firstname { get; set;}
public string lastname { get; set; }
}
然后我必须将我的方法签名更新为如下所示:
public method Blah([FromBody]BlahRequest request) { }
现在,该请求具有从发布请求中填写的firstname
和lastname
属性。
是否有 model 活页夹设置,我可以 go 从简单的 Json ZA8CFDE6331BD59EB26AC96 方法返回到绑定? 或者我是否必须更新我的所有方法签名以包含具有属性的 class?
web api方法怎么调用
原始应用程序是用 Angular 编写的,但我可以通过一个简单的 Fiddler 请求重新创建它:
POST https://localhost:5001/Blah/Posted HTTP/1.1
Host: localhost:5001
Connection: keep-alive
Accept: application/json, text/plain, */*
Content-Type: application/x-www-form-urlencoded
{"firstname":"foo","lastname":"bar"}
在以前版本的 .Net 框架中,控制器的方法会自动解析这些值。 现在,在核心上,它需要传入 model。我尝试了 application/json、multipart/form-data 和 application/x-www-form-urlencoded 作为 Content-type 并且它们都以 null 结尾价值观。
Smallest.Net Core 项目
public class Startup {
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services){
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
app.UseHttpsRedirection();
app.UseRouting();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
}
[Route("[controller]/[action]")]
public class BlahController : ControllerBase {
public object Posted(string firstname, string lastname) {
Console.WriteLine(firstname);
Console.WriteLine(lastname);
return true;
}
}
这取决于内容类型。 您必须使用 application/json 内容类型。 这就是为什么您必须创建一个视图模型并添加 [FromBody] 属性
public method Blah([FromBody]BlahRequest request) { }
但是如果您使用 application/x-www-form-urlencoded 或 multipart/form-data form enctype 或 ajax content-type 那么这将起作用
public method Blah(string firstname, string lastname) {}
查看 .Net 核心源代码的内容,我找到了表单值提供程序如何工作的示例,然后为该项目逆向设计了一个解决方案。 我认为如果一个人刚开始,他们就不必解决这个问题,但是我们正在将基于 Angular 构建的现有 UI 移动到这个新的后端 on.Net 核心上,并且不想将所有服务器端调用重写为模型使用控制器上方法的参数。
因此,只是回顾一下,在以前版本的 .Net 中,您可以在方法签名中使用多个参数将 Json object 发布到 Mvc controller。
public object GetSalesOrders(string dashboardName, int fillableState = 0){}
一个简单的调用方法如下:
POST https://localhost:5001/api/ShippingDashboard/GetSalesOrders HTTP/1.1
Content-Type: application/json
{"dashboardName":"/shippingdashboard/bigshipping","fillableState":1}
查看价值提供者,我创建了自己的价值提供者,并在配置 Startup class 期间将其推到列表的顶部。
services
.AddControllers( options =>{
options.ValueProviderFactories.Insert(0, new JsonBodyValueProviderFactory());
})
JsonBodyValueProviderFactory 是我写的一个工厂 class。 它检查请求,如果内容类型是 application/json,它将向内容添加提供程序:
public class JsonBodyValueProviderFactory : IValueProviderFactory{
public Task CreateValueProviderAsync(ValueProviderFactoryContext context) {
if (context == null) {
throw new ArgumentNullException(nameof(context));
}
var request = context.ActionContext.HttpContext.Request;
if (request.HasJsonContentType()) {
// Allocating a Task only when the body is json data
return AddValueProviderAsync(context);
}
return Task.CompletedTask;
}
private static async Task AddValueProviderAsync(ValueProviderFactoryContext context) {
var request = context.ActionContext.HttpContext.Request;
var body = "";
Dictionary<string,object> asDict = new Dictionary<string, object>();
try {
using (StreamReader stream = new StreamReader(request.Body)){
body = await stream.ReadToEndAsync();
}
var obj = JObject.Parse(body);
foreach(var item in obj.Children()){
asDict.Add(item.Path, item.Values().First());
}
} catch (InvalidDataException ex) {
Console.WriteLine(ex.ToString());
} catch (IOException ex) {
Console.WriteLine(ex.ToString());
}
var valueProvider = new JsonBodyValueProvider(BindingSource.Form, asDict);
context.ValueProviders.Add(valueProvider);
}
}
由于我并不总是知道数据的形状,因此我使用了 Json.Net 的 JObject 并将 Json 主体吸入 JObject。 然后我使用顶级属性并将它们添加到字典中以便于查找。
获取值并响应参数名称的实际 class 是 JsonBodyValueProvider:
public class JsonBodyValueProvider : BindingSourceValueProvider, IEnumerableValueProvider {
private readonly Dictionary<string, object> values;
private PrefixContainer? _prefixContainer;
public JsonBodyValueProvider( BindingSource bindingSource, Dictionary<string,object> values) : base(bindingSource) {
if (bindingSource == null) {
throw new ArgumentNullException(nameof(bindingSource));
}
if (values == null) {
throw new ArgumentNullException(nameof(values));
}
this.values = values;
}
protected PrefixContainer PrefixContainer {
get {
if (_prefixContainer == null) {
_prefixContainer = new PrefixContainer(values.Keys);
}
return _prefixContainer;
}
}
public override bool ContainsPrefix(string prefix) {
return PrefixContainer.ContainsPrefix(prefix);
}
public virtual IDictionary<string, string> GetKeysFromPrefix(string prefix) {
if (prefix == null) {
throw new ArgumentNullException(nameof(prefix));
}
return PrefixContainer.GetKeysFromPrefix(prefix);
}
public override ValueProviderResult GetValue(string key){
if (key == null) {
throw new ArgumentNullException(nameof(key));
}
if (key.Length == 0) {
return ValueProviderResult.None;
}
var _values = values[key];
if (!values.ContainsKey(key)) {
return ValueProviderResult.None;
} else {
return new ValueProviderResult(_values.ToString());
}
}
}
这几乎是 .Net Core 中 FormValueProvider 的抄本,我只是对其进行了调整以使用输入值字典。
现在,我的控制器可以与以前版本的 .Net 保持一致,而无需更改方法签名。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.