你能让 MVC Post 动作只接受表单数据而不接受查询字符串吗

Can you make an MVC Post action only accept form data and not querystring

我正在使用 ASP MVC 5 并且我在我的控制器上有一个动作

[HttpPost()]
public ActionResult MyMethod(string param)
{
    // Action code here
}

我需要它来接受它的数据 "param" 仅来自表单数据,而不是来自查询字符串。

所以像这样的东西应该可以工作

<form method="post" action="http://localhost/Home/MyMethod">
    <input type="hidden" name="param" value="MyValue" />
    <input type="submit" value="Post with token" />
</form>

虽然这不应该(很高兴找不到动作,或者通过提供空模型找到动作)。

<form method="post" action="http://localhost/Home/MyMethod?param=MyValue">
    <input type="submit" value="submit" />
</form>

我看过一些自定义模型活页夹

public class FormOnlyModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        /*
         * http://blogs.msdn.com/b/jmstall/archive/2012/04/20/how-to-bind-to-custom-objects-in-action-signatures-in-mvc-webapi.aspx
         * Add [ModelBinder(typeof(FormOnlyModelBinder))] to parameter
         * e.g. public ActionResult RetreiveBill([ModelBinder(typeof(CModelBinder))] string token)
         * In controllerContext.RequestContext.HttpContext.Request you can see the type, and if the data is in q query string or forms data.
         * Just not sure if this is reliable
         */

        throw new NotImplementedException();
     }
}

但真的不知道从这里到哪里去,或者这是否是做这类事情的可靠方法。

MVC 中的模型绑定过程考虑了称为 Value Providers 的东西。这些是接口 IValueProvider 的实现,其中有一些实现了不同的数据源:FormValueProviderRouteDataValueProviderQueryStringValueProviderHttpFileCollectionValueProvider

默认情况下,模型绑定过程会将它们全部考虑在内,但有一些方法可以限制使用哪些。

  • 手动调用模型绑定过程指定应使用哪个值提供程序

    public ActionResult MyMethod()
    {
        var model = new MyModel();
        if (TryUpdateModel(model, new FormValueProvider(this.ControllerContext)))
        {
            //proceed with post action                
        }
        //validation errors, display same form
        return View(model);
    }
    
  • 将类型为 FormCollection 的参数添加到您的控制器操作,并使用该参数作为值提供程序手动调用模型绑定。 FormCollection 通过仅查看表单参数来实现 IValueProvider,因此它与上面的选项相同(但使您不必创建值提供程序实例)

    public ActionResult MyMethod(FormCollection formData)
    {
        var model = new MyModel();
        if (TryUpdateModel(model, formData))
        {
            //proceed with post action                
        }
        //validation errors, display same form
        return View(model);
    }
    
  • 创建一个仅使用 FormValueProvider 作为其值提供者的模型绑定器。这允许您像往常一样编写控制器操作方法(无需手动调用模型绑定)

    public class FormOnlyModelBinder: DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {            
            ModelBindingContext newBindingContext = new ModelBindingContext()
            {
                ModelMetadata = bindingContext.ModelMetadata,
                ModelName = bindingContext.ModelName,
                ModelState = bindingContext.ModelState,
                PropertyFilter = bindingContext.PropertyFilter,
                FallbackToEmptyPrefix = bindingContext.FallbackToEmptyPrefix,
                ValueProvider = new FormValueProvider(controllerContext),                
            };
    
            return base.BindModel(controllerContext, newBindingContext);
        }
    }
    

所有这些选项都具有相同的效果。仅当我对非常特殊的操作有此要求时,我才会使用第二个选项,在任何其他情况下,我可能会使用自定义模型活页夹。