【问题标题】:MVC Model Binding - Form Post Starts With a NumberMVC 模型绑定 - 表单帖子以数字开头
【发布时间】:2013-10-14 05:57:37
【问题描述】:

我正在尝试为 SagePay 通知创建模型。我知道 MVC 模型绑定器会根据 POST 名称自动绑定我的所有属性。

SagePay 系统传递以下表单名称:

Status
VendorTxCode
VPSTxId
VPSSignature
StatusDetail
AVSCV2
AddressResult
PostCodeResult
CV2Result
GiftAid
3DSecureStatus
CAVV
AddressStatus
PayerStatus
CardType
Last4Digits
DeclineCode
ExpiryDate
FraudResponse
BankAuthCode

我创建了以下类。

public class NotificationModel
{
    public string Status { get; set; }
    public string VendorTxCode { get; set; }
    public string VPSTxId { get; set; }
    public string VPSSignature { get; set; }
    public string StatusDetail { get; set; }
    public string AVSCV2 { get; set; }
    public string AddressResult { get; set; }
    public string PostCodeResult { get; set; }
    public string CV2Result { get; set; }
    public string GiftAid { get; set; }
    // When binding the model will MVC ignore the underscore?
    public string _3DSecureStatus { get; set; }
    public string CAVV { get; set; }
    public string AddressStatus { get; set; }
    public string PayerStatus { get; set; }
    public string CardType { get; set; }
    public string Last4Digits { get; set; }
    public string DeclineCode { get; set; }
    public string ExpiryDate { get; set; }
    public string FraudResponse { get; set; }
    public string BankAuthCode { get; set; }
}

很遗憾,c# 属性名称不能以数字开头。如何让模型绑定器自动将 3DSecureStatus 帖子名称绑定到属性?

【问题讨论】:

  • 是否有理由不能将列命名为 ThreeDSecureStatus(或类似名称)?
  • 你看过this question吗?
  • 我无法控制名称。这些由第 3 方的 API 发布到我的控制器。
  • 如果您想编写自定义模型绑定器,answer 将提供您需要的任何东西

标签: c# asp.net-mvc asp.net-mvc-4 opayo


【解决方案1】:

您可以使用自定义模型绑定器来做到这一点:

public class NotificationModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext,
        ModelBindingContext bindingContext)
    {
        var model = this.CreateModel(controllerContext,
            bindingContext, bindingContext.ModelType);
        bindingContext.ModelMetadata.Model = model;

        var formValue = bindingContext.ValueProvider
            .GetValue(this.FormField).AttemptedValue;

        var target = model.GetType().GetProperty(this.FieldToBindTo);
        target.SetValue(model, formValue);

        // Let the default model binder take care of the rest
        return base.BindModel(controllerContext, bindingContext);
    }

    private readonly string FieldToBindTo = "_3DSecureStatus";
    private readonly string FormField = "3DSecureStatus";
}

我没有添加错误处理,因为这种只处理 1 个字符串的场景是微不足道的。除了字符串的实际值不是您所期望的之外,您可能得到的唯一其他错误(好吧,在这种情况下是异常)是如果在您的模型上找不到目标属性,但显然这是一个修复也很简单。

编辑:实际上,如果在表单上找不到该字段,您也可以获得formValue 的例外情况,但同样,由于这是第 3 方支付系统,我倾向于说不会改变任何一个。

你会这样使用它:

[HttpPost]
public ActionResult Index([ModelBinder(typeof(NotificationModelBinder))]NotificationModel model)
{
    // do stuff
}

【讨论】:

    【解决方案2】:

    如果您希望使用 Web API 控制器执行此操作,以下模型绑定器将有所帮助(实现 System.Web.Http.ModelBinding.IModelBinder

    public class SagePayTransactionNotificationModelBinder : IModelBinder
    {
        public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
            var model = actionContext.Request.Content.ReadAsStringAsync()
                            .ContinueWith(t =>
                                {
                                    var formCollection = HttpUtility.ParseQueryString(t.Result);
    
                                    return new SagePayTransactionNotification
                                    {
                                        VPSProtocol = formCollection["VPSProtocol"],
                                        TxType = formCollection["TxType"],
                                        VendorTxCode = formCollection["VendorTxCode"],
                                        VPSTxId = formCollection["VPSTxId"],
                                        Status = formCollection["Status"],
                                        StatusDetail = formCollection["StatusDetail"],
                                        TxAuthNo = formCollection["TxAuthNo"],
                                        AVSCV2 = formCollection["AVSCV2"],
                                        AddressResult = formCollection["AddressResult"],
                                        PostcodeResult = formCollection["PostcodeResult"],
                                        CV2Result = formCollection["CV2Result"],
                                        GiftAid = formCollection["GiftAid"],
                                        ThreeDSecureStatus = formCollection["3DSecureStatus"],
                                        CAVV = formCollection["CAVV"],
                                        CardType = formCollection["CardType"],
                                        Last4Digits = formCollection["Last4Digits"],
                                        VPSSignature = formCollection["VPSSignature"]
                                    };
                                })
                            .Result;
    
            bindingContext.Model = model;
    
            return true;
        }
    }
    

    这可以像这样应用于您的 Controller 方法:

    [ActionName("Notifications")]
    public string Post([ModelBinder(typeof(SagePayTransactionNotificationModelBinder))] SagePayTransactionNotification notification)
    {
        ...
    }
    

    我知道严格来说这并不是问题中所要求的,但是当我遇到与 OP 完全相同的问题并认为这可能对以后的其他人有用时,我最终得出了这个结论。

    【讨论】:

      猜你喜欢
      • 2014-02-20
      • 1970-01-01
      • 2017-12-01
      • 2016-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-01
      相关资源
      最近更新 更多