【问题标题】:How to use a custom binder to normalize user input?如何使用自定义活页夹来规范用户输入?
【发布时间】:2012-12-05 02:22:28
【问题描述】:

根据用户输入,我想在实体到达控制器的 Action 之前取消实体的某些属性。 在一个粗略的例子中(实际模型要复杂得多),假设我的实体有一个 BillingType 属性,它定义了客户是按月还是每两周计费:

public class BillingMethod
{
    public int Id { get; set; }
    public int BillingTypeValue { get; set; }
    public BillingType BillingType
    {
        get
        {
            return (BillingType)BillingTypeValue;
        } 
        set
        {
            BillingTypeValue = (int)value;
        }
    }

    public int? DayOfMonth { get; set; }
    public int? DayOfFirstFortnight { get; set; }
    public int? DayOfSecondFortnight { get; set; }
}

public enum BillingType
{
    Monthly,
    Fortnightly
}

现在假设用户选择按月收费,然后将 DayOfMonth 属性设置为 15。然后他改变主意,将 Billing Type 设置为每两周一次,并设置两个 fortnigth day 属性,最后提交表单。 我需要的是一种在未使用的属性(在此示例中为 DayOfMonth)到达控制器的操作之前将其无效的方法。

我知道当用户从一种计费类型更改为另一种计费类型时,或者通过拦截表单的 onSubmit 事件时,我可以通过 javascript 执行此操作。甚至在 Action 内部,在将其保存到上下文之前,但我需要一种方法来执行一次并忘记它。

我认为最好的方法是使用自定义模型绑定器,但我没有任何经验。我尝试创建一个新的ModelBindingContext,但我不知道如何在新对象中获取和解析表单数据,所以我显然需要一些指导。

【问题讨论】:

    标签: asp.net-mvc-3 c#-4.0 custom-model-binder


    【解决方案1】:

    我会小心地在自定义模型绑定器中执行此操作。您将遇到的一个问题是每个属性都是单独绑定的,您对顺序没有太多控制权。听起来您需要在绑定完成后根据对象的状态做出一些决定。

    在这种情况下,我可能会使用操作过滤器。见:http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.onactionexecuting(v=vs.98).aspx

    public class BillMethodFilterAttribute : ActionFilterAttribute {
    
         protected override void OnActionExecuting(ActionExecutingContext filterContext) {
            base.OnActionExecuting(filterContext);
               if (filterContext.Controller.ViewData.ModelMetadata.ModelType == typeof(BillingMethod)) {
                   var method = filterContext.Controller.ViewData.Model as BillingMethod;
                   if (method != null) {
                      //assign appropriately - binding is complete, you have full state of the object
                   }
               }
            }        
    }
    
    [BillMethodFilter]
    public abstract class ProjectController : Controller {
    
    
    }
    
    public class SomeController : ProjectController {
    
          public ActionResult SomeAction(BillingMethod method) {
                //before this action runs, the BillMethodFilter should execute and your billing method will be fully initialized correctly
          }
    }
    

    【讨论】:

    • 我在BindModel期间已经绑定了属性...但是你是对的,我想在绑定完成后修改它。但是这个对象将用于许多动作和不同的控制器中,所以我不确定动作过滤器......
    • 您可以创建一个可重复使用的操作过滤器。创建一个从控制器继承的基本控制器,并将其用作所有控制器的基类(无论如何,这通常是个好主意)。然后将您的过滤器属性放在您的基本控制器类上。这样,所有控制器上的所有操作都应该运行您的过滤器。我会更新答案。
    【解决方案2】:

    我设法使用自定义活页夹修改了对象。首先我调用 base.BindModel,然后在返回之前修改属性。

    public class BillingMethodModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext cContext, ModelBindingContext bContext)
        {
            var newBillingMethod = (BillingMethod)base.BindModel(cContext, bContext);
            var bType = newBillingMethod.BillingTypeValue;
            if (bType == (int)BillingType.Monthly)
            {
                newBillingMethod.DayOfFirstFortnight = null;
                newBillingMethod.DayOfSecondFortnight = null;
            }
            else
            {
                newBillingMethod.DayOfMonth = null;
            }
    
            return newBillingMethod;
        }
    }
    

    当然,在 global.asax 的 Application_Start() 中添加自定义绑定器:

    ModelBinders.Binders.Add(typeof(BillingMethod), new BillingMethodModelBinder());
    

    这对我很有用,我会等待一天左右才能接受这个答案,以防万一有人提出更好的解决方案或指出我在使用此方法时可能遇到的任何问题

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-05
      • 2023-04-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多