【问题标题】:MVC5 - How do I define deserialization behavior for action parameter type?MVC5 - 如何定义动作参数类型的反序列化行为?
【发布时间】:2017-11-04 09:37:18
【问题描述】:
    public class LogsController : Controller
    {
        public async Task<ActionResult> User(string id, int? page)
        {
            return await User(id, (Page)page);
        }

        private async Task<ActionResult> User(string id, Page page)
        {
            var data = await Repo.GetData(id, page, 10);
            var model = new UserLogs
            {
                User = id,
                Events = data,
                Pager = new PagerInput(page, 10, data.TotalCount)
            };

            return View("UserGrid", model);
        }
    }

My Page 类提供了有用的功能,例如确保页面永远不会小于 1 等。但是,如果我的公共操作将 Page 作为参数,则无论表单或查询字符串中的值如何,它始终为 null,即使我已经定义了与 int 之间的隐式转换。

无论如何我可以告诉 MVC 如何反序列化 Page 类型吗?这样提供某种方法/ctor来从字符串转换?

我想在实际的页面类型上定义它,因为我将在多个控制器/动作中使用它。

我会很高兴不需要双重动作定义。

【问题讨论】:

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


    【解决方案1】:

    您必须创建自己的模型绑定器。

    Guid 示例:

    public class GuidModelBinder : DefaultModelBinder
    {
        public override object BindModel(
                ControllerContext controllerContext,
                ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType == typeof(Guid) ||
                bindingContext.ModelType == typeof(Guid?))
            {
                Guid result;
    
                var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
    
                if (valueResult.AttemptedValue == null &&
                        bindingContext.ModelType == typeof(Guid))
                    return Guid.Empty;
    
                if (valueResult.AttemptedValue == null &&
                        bindingContext.ModelType == typeof(Guid?))
                    return null;
    
                if (Guid.TryParse(valueResult.AttemptedValue, out result))
                    return result;
            }
    
            return base.BindModel(controllerContext, bindingContext);
        }
    }
    

    创建绑定配置类后:

    public class BindingConfig
    {
        public static void RegisterBinders(ModelBinderDictionary binders)
        {
            binders.Add(typeof(Guid), new GuidModelBinder());
            binders.Add(typeof(Guid?), new GuidModelBinder());
        }
    }
    

    最后从 Application_Start 调用 RegisterBinders:

    protected void Application_Start()
    {
        BindingConfig.RegisterBinders(ModelBinders.Binders);
    }
    

    【讨论】:

    • 谢谢,这让我开始使用更通用的解决方案。
    【解决方案2】:

    此自定义模型绑定器允许您通过简单地定义带有字符串参数的构造函数来定义任何类型的反序列化行为:

    public class CustomModelBinder : DefaultModelBinder
    {
        static Assembly _assembly = typeof(CustomModelBinder).Assembly;
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {   
            if (_assembly.Equals(bindingContext.ModelType.Assembly))
            {
                var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
                var ctor = bindingContext.ModelType.GetConstructor(new Type[] { typeof(string) });
                if (ctor != null && value != null)
                    return ctor.Invoke(new object[] { value.AttemptedValue });
            }
            return base.BindModel(controllerContext, bindingContext);
        }
    }
    

    然后注册...

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        ModelBinders.Binders.DefaultBinder = new CustomModelBinder();
    }
    

    【讨论】:

      猜你喜欢
      • 2021-04-22
      • 2022-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-17
      • 2017-01-04
      • 2018-01-09
      • 2018-07-05
      相关资源
      最近更新 更多