繁体   English   中英

绑定.NET Core Json到简单参数

[英]Binding .NET Core Json to simple parameters

我过去只是将简单的 json 对象发布到 ASP.NET MVC 控制器,绑定引擎会将主体解析为方法的简单参数:

{
    "firstname" : "foo",
    "lastname" : "bar"
}

我可以有这样的 MVC controller:

public method Blah(string firstname, string lastname) {}

firstnamelastname会自动从 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) { }

现在,该请求具有从发布请求中填写的firstnamelastname属性。

是否有 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM