【问题标题】:Custom Model Binder for inheritance/typediscrimination用于继承/类型区分的自定义模型绑定器
【发布时间】:2018-09-07 18:16:38
【问题描述】:

很抱歉,如果这个问题已经被问及回答了一百万次,但我似乎无法在 Google 或 SO 上找到解决方案。

在 ASP.NET Core 中,是否可以编写自定义模型绑定器或类似的东西,这将允许我在 Web API 中获得一些继承支持?

基本上我有这样的课程:

public class Order {
   public Guid Id { get; set; }
   public PaymentContainer PaymentParameters { get; set; }
}

还有一个容器类

public class PaymentContainer 
{
    public string Type { get; set; }

    public IPaymentParameters Value { get; set; }
}

还有一些实现IPaymentParameters接口的类,以及控制器方法如:

[HttpPost]
public IActionResult CreateOrder([FromBody]Order order)
{
}

我非常希望客户端能够以 json 格式发送,例如:

{
  "id" : "1234-..-1234",
  "paymentParameters" : {
      "type": "SpecialParameters1",
      "value" : { /* instance of specialparameters1 here */ }
  }
}

然后让“value”属性成为“SpecialParameters1”的一个实例,一旦它到达控制器方法。

我认为可以编写一个模型绑定器,但我无法完全理解它是如何完成的。

注意:我知道可以更改 Json.Net TypeNameHandling 的方法,但我不想弄乱所有其他依赖 json 序列化程序的东西,而只是设置它到 Auto 或 All 将为远程代码执行开辟一些途径。

澄清:第一次回答后的小更新 目标是拥有多个参数实例,以便以下输入也“有效”

{
  "id" : "1234-..-1234",
  "paymentParameters" : {
      "type": "SpecialParameters2",
      "value" : { /* instance of specialparameters2 here */ }
  }
}

当然我也会上课

public class SpecialParameters1 : IPaymentParameters{}
public class SpecialParameters2 : IPaymentParameters{}

【问题讨论】:

标签: c# asp.net-core


【解决方案1】:

是的,它 ASP.NET Core 非常擅长模型绑定,我将为您设置示例(只是重构了一点您的类 - 不确定您需要做什么,但作为一个原理的演示,我觉得很合适)

在 View 中,您设置如下:

<h2>orders with ajax</h2>
<dl>
    <dt>Order Id:</dt>
    <dd id="order-id">D3BCDEEE-AE26-4B20-9170-D1CA01B52CD4</dd>

    <dt>Payment container - Type</dt>
    <dd id="order-paymentcontainer-type">Card payment</dd>

    <dt>Payment Parameters - Name</dt>
    <dd id="order-paymentcontainer-paymentParameters-name">Card payment nr. 1</dd>

    <dt>Payment Parameters - Provider</dt>
    <dd id="order-paymentcontainer-paymentParameters-provider">Big Bank</dd>
</dl>
<a id="submit-button">Process order</a>


@section Scripts {
    <script src="~/js/paymentTest.js"></script>
}

然后你设置类:

public class Order
{
    public Guid Id { get; set; }
    public PaymentContainer PaymentContainer { get; set; }
}

public class PaymentContainer
{
    public string Type { get; set; }
    public PaymentParameters PaymentParameters { get; set; }
}

public class PaymentParameters
{
    public string Name { get; set; }
    public string Provider { get; set; }
}

然后写在你的 paymentTest.cs 中

var order = {
        Id: "",
        PaymentContainer: {
            Type: "",
            PaymentParameters: {
                Name: "",
                Provider: ""
            }
        }
    };

order.Id = document.getElementById("order-id").innerHTML;
order.PaymentContainer.Type = document.getElementById("order-paymentcontainer-type").innerHTML;
order.PaymentContainer.PaymentParameters.Name = document.getElementById("order-paymentcontainer-paymentParameters-name").innerHTML;
order.PaymentContainer.PaymentParameters.Provider = document.getElementById("order-paymentcontainer-paymentParameters-provider").innerHTML;

$("#submit-button").click(function () {
    $.ajax({
        url: 'api/orders',
        type: 'POST',
        dataType: "json",
        contentType: 'application/json',
        data: JSON.stringify(order),
        success: function (data) {
            location.reload();
        }
    });
});

最后在控制器设置方法中进行绑定

[Route("api/orders")]
[HttpPost]
public async Task ProcessOrder([FromBody] Order order)
{
    var orders = new List<Order>();
    orders.Add(order);

    // and some other code ...
}

【讨论】:

  • 感谢您尝试回答,但这并不能真正解决我的问题 - 请参阅问题的澄清/更新。
  • 我不确定您要做什么,所以我设置了一个小代码案例,允许进行一些测试。请参阅下面的答案中的解决方法。
【解决方案2】:

如果我错了,请纠正我 - 这里的主要问题是您无法将数据从 View 返回到控制器?

您无法绑定,因为您的 Order 类包含接口(间接),而 MVC 无法绑定接口。 没关系。因为接口抽象行为而不是数据。

此问题的潜在解决方法是在 CreateOrder 方法中使用 OrderViewModel 而不是 Order 类型。 即:

public IActionResult CreateOrder([FromBody]OrderViewModel order)

那么 OrderViewModel 将有 PaymentContainerViewModel 而不是 PaymentContainer, 它会有具体的 PaymentParameters 值 而不是接口 IPaymentParameters 的值,比绑定好。

【讨论】:

  • 嘿@juramarin :)。我会将这个答案与您的第二个答案结合起来 :) 也许用不同的标题 Answer 1Answer 2 将它们分开。只是一个想法。
  • 是的,你的权利,将在未来的答案中这样做。 :)
猜你喜欢
  • 1970-01-01
  • 2013-05-20
  • 2013-03-09
  • 1970-01-01
  • 2021-09-17
  • 2011-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多