通过 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;
...
modelName
是 viewModel
(老实说,我不知道为什么,但它有效...)
我的控制器看起来像这样
[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: IGeometry
或 Rectangle: IGeometry
或 Polygon: IGeometry
。 IGeometry
本身有方法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 而不是字符串(易于转换 :-))
我想通过 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;
...
modelName
是 viewModel
(老实说,我不知道为什么,但它有效...)
我的控制器看起来像这样
[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: IGeometry
或 Rectangle: IGeometry
或 Polygon: IGeometry
。 IGeometry
本身有方法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 而不是字符串(易于转换 :-))