用于继承/类型歧视的自定义模型绑定器

问题描述 投票:3回答:2

很抱歉,如果已经被要求回答了一百万次,但我似乎无法在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 */ }
  }
}

然后,一旦“SpecialParameters1”到达控制器方法,它就会成为“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
2个回答
0
投票

是的,ASP.NET Core非常擅长模型绑定,我会为你设置一个例子(刚刚重构了一些你的类 - 不确定你需要做什么,但作为一个原则的演示我认为它会适合)

在视图中,您可以设置如下内容:

<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 ...
}

0
投票

请纠正我如果我错了 - 这里的主要问题是你不能将View中的数据绑定到控制器?

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

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

public IActionResult CreateOrder([FromBody]OrderViewModel order)

然后OrderViewModel将具有PaymentContainerViewModel而不是PaymentContainer,它将具有具体的PaymentParameters值而不是接口IPaymentParameters值,并且比绑定更好。

© www.soinside.com 2019 - 2024. All rights reserved.