ModelState.isValid为false,因为Id模型属性与传递的参数冲突

时间:2013-01-22 20:41:40

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

我对MVC还很陌生,并且很难弄清楚如何最好地解决这个问题。

我有一个自动搭建的"Create" (GET)操作的修改版本,它带有一个可选参数(下拉列表的默认值)。然后它将其表单提交给"Create" (HTTPPost)操作。

public ActionResult Create(string id = null)
{ 
    //use id to pick a default item in a drop down list.
}

[HttpPost]
public ActionResult Create(Registration registration)
{ 
    //this method doesn't care one iota what was passed to the GET method
    // but registration.id is invalid because it's trying to set it to the value that was passed to the get method.  If id in the get method is not passed, then everything is fine
}

问题是,如果将值传递给GET创建操作,则ModelState.isValid操作上的POST create为false,因为它试图将传递给GET操作的值填充到POST动作中模型的主键(Id)字段(由于它是自动生成的,因此应该省略。

我知道我可以将主键列名称更改为Id之外的其他名称,但我无法控制数据库,并试图找出解决此问题的其他方法。

如果我尝试将GET操作中的参数从“id”重命名为其他内容,那么我必须将链接从CREATE/paramValue更改为CREATE/?paramName=paramValue,我宁愿不这样做。 (虽然我会这是唯一简单的方法)。

有没有办法让POST操作忽略传递给GET操作的值?

2 个答案:

答案 0 :(得分:2)

尝试使用BindAttribute

[HttpPost]
public ActionResult Create([Bind(Exclude = "RegistrationId")]Registration registration)
{ 

}

绑定到模型时,它将省略RegistrationId以表格形式发布...

答案 1 :(得分:2)

我认为你要做的是使用它应该创建的对象类型的ID来参数化控制器的Create方法。

虽然ASP.NET MVC中没有关于参数名称的硬性和快速约定,但我认为大多数人都希望名为id的参数引用正在处理的对象,而不是它的属性。

考虑到这一点,我建议您做以下事情:

  1. 将Create方法的GET版本中的参数重命名为类似typeId或类似的东西,因此签名看起来像public ActionResult Create(string typeId = null)

  2. MvcApplication添加自定义路线,这样您就可以获得格式正确的/Create/typeId,而不是强制拥有/Create?typeId=value

  3. 您可以通过在默认路由之前添加以下代码行来执行此操作:

    routes.MapRoute
    (
        name: "CreateGet",
        url: "Controller/Create/{typeId}",
        defaults: new
        {
            controller = "Controller",
            action = "Create",
            typeId = UrlParameter.Optional
        }
    );
    

    默认路线如下所示:

    routes.MapRoute
    (
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new
        {
            controller = "Site",
            action = "Index",
            id = UrlParameter.Optional
        }
    );
    

    我认为这可以解决您的问题,但请确保我建议的自定义路线位于默认路线之前。

    <强>更新

    你有一个Get方法和一个Post方法。

    Get方法(可能)创建一个视图模型对象并将其发送到呈现它的视图。或者,您的视图可以在HTML中进行硬编码的各种输入或通过AJAX调用注入。无论他们如何到达那里,你在Web浏览器中呈现的渲染视图都是这样的:

    <html>
    <head />
    <body>
    <form>
    <input id="Input1" ... />
    <select id="Select1" ... />
    </form>
    </body>
    </html>
    

    上面是一个非常简化的HTML结构。

    输入的ID与视图模型对象中的属性名称匹配,这些属性将在回发此页面时创建,即Registration类的实例。

    在返回的路上(对于Post方法),模型绑定器从请求中获取值(查询字符串参数,表单值等),并将它们绑定回给Post方法的模型对象。

    在您的情况下,最有可能发生的事情是,id在Get方法(和渲染视图)中的含义与id在{{1}中的含义不匹配赋予Post方法的对象。

    不匹配的原因是Registration属性名称由数据库决定,Registration.Id由默认路由决定,但它们实际上是不同的东西,所以应该以不同的名称命名。

    这使得您需要拥有/ Create / id表单的干净URL,这可以通过自定义路由轻松完成。

    现在,如果你真的想从模型绑定逻辑中排除一个参数,那么你应该使用另一个使用Post方法的Create(string id)参数上的属性来明确告诉{{1}的答案。从绑定逻辑中排除该参数。

    一旦你这样做了,它将绑定的属性将保留其在实例化和运行构造函数后由CLR给它的默认值。

    ASP.NET MVC还支持自定义模型绑定器,如果需要,可以显着改变模型绑定逻辑,但这并不适用于您的情况。