在MVC 2中处理和验证POST数据的现代方法

时间:2010-05-04 23:20:46

标签: asp.net asp.net-mvc-2

有很多文章专门讨论MVC中的数据,而MVC 2则没有。

所以我的问题是:处理POST查询并验证它的正确方法是什么。

假设我们有2个动作。它们都在同一个实体上运行,但每个动作都有自己独立的对象属性集,应该以自动方式绑定。例如:

  • 操作“A”应仅绑定对象的“Name”属性,取自POST-request
  • 操作“B”应仅绑定对象的“Date”属性,取自POST-request

据我了解 - 在这种情况下我们不能使用Bind属性。

那么 - MVC2中处理POST数据并可能验证它的最佳实践是什么?

UPD
执行操作后 - 将对对象应用其他逻辑,以使它们变为有效并准备存储在持久层中。对于操作“A” - 它将日期设置为当前日期。

2 个答案:

答案 0 :(得分:3)

我个人不喜欢使用域模型类作为我的视图模型。我发现它会导致验证,格式化问题,并且通常会感觉不对。事实上,我根本不会在我的视图模型上使用DateTime属性(我将其格式化为控制器中的字符串)。

我会使用两个单独的视图模型,每个模型都有验证属性,作为主视图模型的属性公开:

注意:我已经离开了如何将发布的视图模型与主视图模型结合起来作为练习,因为有几种方法可以接近它

public class ActionAViewModel
{
    [Required(ErrorMessage="Please enter your name")]
    public string Name { get; set; }
}

public class ActionBViewModel
{
    [Required(ErrorMessage="Please enter your date")]
    // You could use a regex or custom attribute to do date validation,
    // allowing you to have a custom error message for badly formatted
    // dates
    public string Date { get; set; }
}

public class PageViewModel
{
    public ActionAViewModel ActionA { get; set; }
    public ActionBViewModel ActionB { get; set; }
}

public class PageController
{
    public ActionResult Index()
    {
        var viewModel = new PageViewModel
        {
            ActionA = new ActionAViewModel { Name = "Test" }
            ActionB = new ActionBViewModel { Date = DateTime.Today.ToString(); }
        };

        return View(viewModel);
    }

    // The [Bind] prefix is there for when you use 
    // <%= Html.TextBoxFor(x => x.ActionA.Name) %>
    public ActionResult ActionA(
        [Bind(Prefix="ActionA")] ActionAViewModel viewModel)
    {
        if (ModelState.IsValid)
        {
            // Load model, update the Name, and commit the change
        }
        else
        {
            // Display Index with viewModel
            // and default ActionBViewModel
        }
    }

    public ActionResult ActionB(
        [Bind(Prefix="ActionB")] ActionBViewModel viewModel)
    {
        if (ModelState.IsValid)
        {
            // Load model, update the Date, and commit the change
        }
        else
        {
            // Display Index with viewModel
            // and default ActionAViewModel
        }
    }
}

答案 1 :(得分:2)

处理POST数据和添加验证的一种可能方法是使用自定义模型绑定器。 以下是我最近用于向POST表单数据添加自定义验证的一小部分示例:

public class Customer
{
    public string Name { get; set; }
    public DateTime Date { get; set; }
}


public class PageController : Controller
{
    [HttpPost]
    public ActionResult ActionA(Customer customer)
    {
        if(ModelState.IsValid) {
        //do something with the customer
        }
    }

    [HttpPost]
    public ActionResult ActionB(Customer customer)
    {
       if(ModelState.IsValid) { 
       //do something with the customer
       }
    }
}

CustomerModelBinder将是这样的:

    public class CustomerModelBinder : DefaultModelBinder
{
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
    {
        if (propertyDescriptor.Name == "Name") //or date or whatever else you want
        {


            //Access your Name property with valueprovider and do some magic before you bind it to the model.
            //To add validation errors do (simple stuff)
            if(string.IsNullOrEmpty(bindingContext.ValueProvider.GetValue("Name").AttemptedValue))
                bindingContext.ModelState.AddModelError("Name", "Please enter a valid name");

            //Any complex validation
        }
        else
        {
            //call the usual binder otherwise. I noticed that in this way you can use DataAnnotations as well.
            base.BindProperty(controllerContext, bindingContext, propertyDescriptor); 
        }
    }

并在global.asax put

ModelBinders.Binders.Add(typeof(Customer), new CustomerModelBinder());

如果你不想在调用ActionB时绑定Name属性(只是Date),那么只需再创建一个自定义Model Binder,并在“if”语句中,返回null,或者已经存在的值,或者其他你要。然后在控制器中输入:

[HttpPost]
public ActionResult([ModelBinder(typeof(CustomerAModelBinder))] Customer customer)

[HttpPost]
public ActionResult([ModelBinder(typeof(CustomerBModelBinder))] Customer customer)

其中customerAmodelbinder仅绑定name而customerBmodelbinder将仅绑定日期。

这是我发现的最简单的方法,验证模型绑定,并且我已经使用复杂的视图模型获得了一些非常酷的结果。我打赌那里有一些我错过的东西,也许更专家可以回答。 希望我的问题正确......:)