MVC模型绑定-表单发布以数字开头

4

我正在尝试创建一个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的提交名称绑定到属性?


1
你不能简单地将该列命名为ThreeDSecureStatus或类似的名称吗?是否有什么原因呢? - cbeckner
你看过这个问题了吗? - glautrou
我无法控制这些名称。它们是由第三方API POST到我的控制器的。 - Xpanse
如果你想编写一个自定义的模型绑定器,这个答案将提供你所需的一切。 - Simon Rapilly
2个回答

2
你可以使用自定义模型绑定器来完成此操作:
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";
}

我没有添加错误处理,因为这种处理只涉及一个字符串的情况是微不足道的。除了实际值不是您期望的内容之外,您唯一可能遇到的其他错误(或异常)是如果在您的模型上找不到目标属性,但显然也很容易解决。
编辑:实际上,如果表单上找不到字段,您还可以获得formValue的异常,但同样,由于这是第三方支付系统,我倾向于说这也不会改变。
使用方法如下:
[HttpPost]
public ActionResult Index([ModelBinder(typeof(NotificationModelBinder))]NotificationModel model)
{
    // do stuff
}

1
如果您希望使用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;
    }
}

这可以应用于您的控制器方法,如下所示:
[ActionName("Notifications")]
public string Post([ModelBinder(typeof(SagePayTransactionNotificationModelBinder))] SagePayTransactionNotification notification)
{
    ...
}

我知道这不完全符合问题所要求的,但当我遇到与OP完全相同的问题时,我就来到了这里,我认为这可能对以后的某个人有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接