Я бы хотел связать объект в контроллере через тело 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 возвращает ноль ... Что я делаю не так?
ОБНОВЛЕНИЕ
Пример;Предположим, у вас есть интерфейс 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));
}
проблема в том, что вы не можете привязать интерфейс, который выдает ошибку, вам нужноучебный класс!Вот где используется специальное связующее.Предположим, что ваш 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 Core, у вас нет actionContext, только bindingContext, где я не могу найти тело HTTP-сообщения.
ОБНОВЛЕНИЕ 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();
...
Затем каждый класс просто вычисляет площадь соответственно, поэтому
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;
}