通过 ASP.Net Core 中的 body 自定义模型绑定

Custom model binding through body in ASP.Net Core

我想通过 HTTP Post 的主体在控制器中绑定一个对象。

它是这样工作的

public class MyModelBinder : IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
                throw new ArgumentNullException("No context found");

            string modelName = bindingContext.ModelName;
            if (String.IsNullOrEmpty(modelName)) {
                bindingContext.Result = ModelBindingResult.Failed();
                return Task.CompletedTask;
            }

            string value = bindingContext.ValueProvider.GetValue(modelName).FirstValue;
...

modelNameviewModel(老实说,我不知道为什么,但它有效...)

我的控制器看起来像这样

    [HttpPost]
    [Route("my/route")]
    public IActionResult CalcAc([ModelBinder(BinderType = typeof(MyModelBinder))]IViewModel viewModel)
    {
   ....

即它有效,当我发出这个 HTTP-Post 请求

url/my/route?viewModel=URLparsedJSON

但是我想通过请求的主体传递它,即

public IActionResult Calc([FromBody][ModelBinder(BinderType = typeof(MyModelBinder))]IViewModel viewModel)

然后在我的 Modelbinder 中,modelName 是“”并且 ValueProvider 产生 null...我做错了什么?

更新

示例;假设您有一个接口 IGeometry 和许多不同 2D 形状的实现,例如 Circle: IGeometryRectangle: IGeometryPolygon: IGeometryIGeometry本身有方法decimal getArea()。现在,我的 URL 将计算实现 IGeometry 的任何形状的面积,看起来像这样

    [HttpPost]
    [Route("geometry/calcArea")]
    public IActionResult CalcArea([FromBody]IGeometry geometricObject)
    {
         return Ok(geometricObject.getArea());
         // or for sake of completness
         // return Ok(service.getArea(geometricObject));
    }

问题是,您无法绑定到接口,这会产生错误,您需要 class!这就是使用自定义模型绑定器的地方。假设您的 IGeometry 也有以下 属性 string Type {get; set;} 在自定义模型绑定中,您只需在传递的 json 中搜索该类型并将其绑定到正确的实现。像

if (bodyContent is Rectangle) // that doesn't work ofc, but you get the point
var boundObject = Newtonsoft.Json.JsonConvert.DeserializeObject<Rectangle>(jsonString);

ASP.Net EF

在 ASP.Net EF 中,自定义模型绑定如下所示

 public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)

在这里你可以得到 HTTPPost 请求的正文,就像这样

 string json = actionContext.Request.Content.ReadAsStringAsync().Result;

在 ASP.Net 核心你没有 actionContext,只有 bindingContext 我找不到 HTTP 的主体 Post。

更新 2

好的,我找到正文了,看到接受的答案。现在在控制器方法中,我真的有一个来自 IGeometry 类型(接口)的对象,它在自定义模型活页夹中实例化了!我的控制器方法如下所示:

    [HttpPost]
    [Route("geometry/calcArea")]
    public IActionResult CalcArea([FromBody]IGeometry geometricObject)
    {
         return Ok(service.getArea(geometricObject));
    }

我注入的服务是这样的

    public decimal getArea(IGeometry viewModel)
    {
        return viewModel.calcArea();
    }

另一方面,IGeometry 看起来像这样

public interface IGeometry
{
    string Type { get; set; } // I use this to correctly bind to each implementation
    decimal calcArea();

...

每个class然后简单地计算相应的面积,所以

public class Rectangle : IGeometry
{
    public string Type {get; set; }
    public decimal b0 { get; set; }
    public decimal h0 { get; set; }

    public decimal calcArea()
    {
        return b0 * h0;
    }

public class Circle : IGeometry
{
    public string Type {get; set; }
    public decimal radius { get; set; }

    public decimal calcArea()
    {
        return radius*radius*Math.Pi;
    }

我找到了解决办法。使用 ASP.NET 核心的 HTTP Post 请求的主体可以使用这行代码

在自定义模型绑定器中获得
string json;
using (var reader = new StreamReader(bindingContext.ActionContext.HttpContext.Request.Body, Encoding.UTF8))
   json = reader.ReadToEnd();

我在查看旧的 EF 项目后找到了解决方案。主体位于 ActionContext 内部,它作为 BindModel 方法中的参数单独传递。我发现相同的 ActionContext 是 ASP.Net Core 中 ModelBindingContext 的一部分,在那里你得到一个 IO.Stream 而不是字符串(易于转换 :-))