MVC3不能正确识别JSON中的Enum枚举值

原文:MVC3不能正确识别JSON中的Enum枚举值

一、背景

在MVC3项目里,如果Action的参数中有Enum枚举作为对象属性的话,使用POST方法提交过来的JSON数据中的枚举值却无法正确被识别对应的枚举值。

二、Demo演示

为了说明问题,我使用MVC3项目创建Controller,并且创建如下代码演示:

    //交通方式枚举
    public enum TrafficEnum
    {
        Bus = 0,
        Boat = 1,
        Bike = 2,
    }
    public class Person
    {
        public int ID { get; set; }
        public TrafficEnum Traffic { get; set; }
    }

    public class DemoController : Controller
    {
        public ActionResult Index(Person p)
        {
            return View();
        }
    }

      网站生成成功之后,就可以使用Fiddler来发送HTTP POST请求了,注意需要的是,要在Request Headers加上请求头content-type:application/json,这样才能通知服务器端Request Body里的内容为JSON格式。

      点击右上角的Execute执行HTTP请求,在程序断点情况下,查看参数p,属性ID已经正确的被识别到了值为9999,而枚举值属性Traffic却被错认为枚举中的首个值Bus,这俨然是错误的,纵使你将Traffic修改成Bike,也就是值等于2,结果也是一样。

 

三、解决方法

方法一:

升级MVC4,亲测在MVC4项目下,这个问题已经被修复了;

方法二:

假若因为各种原因,项目不想或者不能升级为MVC4,可以在MVC3项目上做些改动,亦可修复这个问题,

1、在项目中,新建一个类,加入以下代码,需要引用一下 using System.ComponentModel;  using System.Web.Mvc; 命名空间;

    /// <summary>
    /// 处理在MVC3下,提交的JSON枚举值在Controller不能识别的问题
    /// </summary>
    public class EnumConverterModelBinder : DefaultModelBinder
    {
        protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
        {
            var propertyType = propertyDescriptor.PropertyType;
            if (propertyType.IsEnum)
            {
                var providerValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
                if (null != providerValue)
                {
                    var value = providerValue.RawValue;
                    if (null != value)
                    {
                        var valueType = value.GetType();
                        if (!valueType.IsEnum)
                        {
                            return Enum.ToObject(propertyType, value);
                        }
                    }
                }
            }
            return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
        }
    }

2、在Global.asax的Application_Start方法中,进行EnumConverterModelBinder类的实例化操作:

       protected void Application_Start()
        {
            //处理在MVC3下,提交的JSON枚举值在Controller不能识别的问题
            ModelBinders.Binders.DefaultBinder = new EnumConverterModelBinder();
        }

进行配置改造之后,我再次生成网站,重新发送HTTP请求看,MVC Action中的参数里的枚举就能被正确的识别到了。

 

四、研究

我觉得这应该是mvc3里面一个小小的缺陷吧,随着mvc的升级,这已经在新版本里被完善修复了,可还用着mvc3的人如果在项目中遇到这个问题,可以研究一下。

遇到一个问题,去百度谷歌找解决方案是可以,但是复制粘贴完代码之后,最好问下自己,为什么这样可以解决问题。

从现象和解决代码中猜想,应该是在MVC生命周期中的Model Binders 这一环节出了问题。

     因为MVC已经开源了,所以我尝试着调试源码,首先下载MVC3的源码,其他项目可以移除,只保留红色框中的项目即可,然后新建一个MVC3测试项目,并且将此测试项目的system.web.mvc引用移除,转而引用本解决方案中的system.web.mvc 项目,这样子,我们才可以对MVC源码进行调试操作。

搜回来的代码中可知,我们自定义的类继承DefaultModelBinder父类,并且重写了GetPropertyValue方法,那我们就从这点开始,在MVC3源码中的System.Web.MVC项目中找到该类,在此方法上插入断点。

F5调试程序,发送一个POST请求。

其实BindProperty方法是会被多次执行的,BindProperties方法会对请求的实体类的属性进行遍历,每一个属性都要经过BindProperty方法的处理;

现在已经截获到第一个属性ID了。

紧接着,程序进入propertyBinder.BindModel 方法。

只贴部分关键代码了,通过bindingContext的ValueProvider 获得属性的相关信息,如果不等于null的话,转到执行BindSimpleModel 方法。

BindSimpleModel方法里,首先通过Type.IsInstanceOfType方法判断确定指定的对象是否是当前 Type 的实例,如果是,则直接返回rawValue,这里的属性类型是Int32类型,返回True符合条件,所以直接把rawValue给返回去了。

第一个Int32类型属性的部分关键代码执行到这里就已经确认到值了,接下来,我们看出了问题的Enum枚举类型属性。

 循环来到了第二个属性了,这时我留意到有个Model属性,对比Int32类型执行的时候,这个属性当时为0,而此时则为Bus,可见这是一个默认值,指定枚举中值为0的那个类型(即使你不为枚举显式指定值),同样的,经过BindModel方法来到了BindSimpleModel方法。

此时,对比Int32类型的属性ID,这次ModelType.IsInstanceOfType(valueProvideResult.RawValue)为False,并且接下来不是string类型就执行以下的判断,也不是数组类型,所以,来到了最后一个,根据绿色的注释可以看出,这应该是一个判断是否collection集合类型的方法,Enum都不是,所以,返回了Null。

这时,Type collectionType变量为Null,执行最后一个case 3

ConvertProviderResult方法里,也进行了一系列的类型判断转换,目的就是将JSON中的数字类型转换成枚举值,但是执行过程中抛出异常了,原因是

“No type converter can convert between these types ” 也就是说,在MVC3的机制中,并没有相应的type converter来处理数值与枚举的对应。

经过以上这些处理方法,都没完成把对应的值确认下来,怎么给原来的BindProperty 老大方法交差呢,所以,小的只好将Value=Null 和 modelState.Errors 模型错误状态信息如实带回去了,让老大决定怎么做,老大后面处理这里有点绕,但是我看源码估计也是拿默认值来充当Value了,所以就造成了JSON传过来的值与对应枚举的值不对应的情况,无论传什么值,结果都是第一个枚举的值。

 

 五、总结

这篇文章只是我在工作上遇到的一个小问题,然后有点小兴趣就从源码的角度上来研究和分析,缺乏理论的依据,因为之前没有很深入的去研究MVC的底层运行机制与生命周期,所以这方面还需要得加强学习一下,如果你也有兴趣,可以下载我修改好的源码来分析一下,甚至可以下载MVC4的源码来进行对比。

时间: 2024-09-24 05:19:12

MVC3不能正确识别JSON中的Enum枚举值的相关文章

获取Enum枚举值描述的几法方法

原文:获取Enum枚举值描述的几法方法 1.定义枚举时直接用中文 由于VS对中文支持的很不错,所以很多程序员都采用了此方案. 缺点:1.不适合多语言 2.感觉不太完美,毕竟大部分程序员大部分代码都使用英文 2.利用自定义属性定义枚举值的描述(博客园-大尾巴狼) 缺点:不适合多语言 原文:http://www.cnblogs.com/hilite/archive/2006/03/28/360793.html 枚举定义: [EnumDescription("订单.订单中的产品.产品项的状态.&quo

Python中模拟enum枚举类型的5种方法分享_python

以下几种方法来模拟enum:(感觉方法一简单实用) 复制代码 代码如下: # way1 class Directions:     up = 0     down = 1     left = 2     right =3     print Directions.down # way2 dirUp, dirDown, dirLeft, dirRight = range(4) print dirDown # way3 import collections dircoll=collections.

JS 遍历JSON中每个key值

JS 遍历JSON中的每个key值,可以按键值对进行存储:   var myVar = { typeA: { option1: "one", option2: "two" } } Object.keys(myVar).forEach(function(k) { if(k === "typeA") { // do stuff } else if (k === "typeB") { // do more stuff } else

USB硬盘不被系统正确识别问题

1. 如果系统装的是win98,如不能被正确识别(即使安装了USB2.0通用驱动也识别不了),这种情况下要检查一下你的移动硬盘是否供电不足,如果供电不足就会出现"咳咳"的声响. 2. 如果你的系统装的是win2K/winXP的话,系统识别到最后设备管理器里还显示"!惊叹号",此种情况也要考虑是否供电的问题,因为XP默认识别几乎所有的移动设备. 3. 基于供电不足的问题采取的办法如下: 1) 检查一下你是否使用USB前置接口,如果是插在前置的USB口不识别的话,请换着

让电脑正确识别无线网卡

无线上网的"门槛"越来越低了,只要手头有无线网卡和无线接入点,那就具备无线上网的条件了.正确安装并设置好无线网卡的上网参数,是确保高效接入无线网络的前提;不过,在与无线网络"亲密接触"的过程中,常常会遇到系统无法识别无线网卡的现象,这样一来就会给设置无线网络参数带来麻烦.要是你面对无线网卡被系统无法识别时,该如何应对呢?倘若还没有什么主意的话,那就来看看本文下面提供的建议吧. 1.网卡插入是否到位 目前无线网卡主要有PCI.USB.PCMCIA三种接口类型,无论哪一

JSON中双引号的轮回使用过程中一定要小心

 如果JSON对象中有属性是包含双引号当转换成字符串形式,将自动加上反斜线,详细请祥看本文 1. 如果JSON对象中有属性是包含双引号的,比如   代码如下: {  "description": "25""  }    如果转换成字符串形式,将自动加上反斜线,变为"25"", 然后传递给REST API,保存到MongoDB中.    此时如果使用MongoDB的shell显示该数据,为"25""

XP系统没有正确识别内置调制解调器

  问:我原来在电脑的C分区中安装了Windows 98,现在又在D分区中安装了Windows XP.可发现在Windows XP下的"设备管理器"里没有出现我安装的内置调制解调器的选项,而且"设备管理器"里"PCI简易通讯"前还出现了一个问号.请问如何解决这个问题? 答:这种现象一般是因为Windows XP没有正确识别和安装Modem的驱动程序造成的.您在安装Windows XP前最好打开机箱,先将Modem拔出,安装好系统后再将Modem插

JSON中双引号的轮回使用过程中一定要小心_javascript技巧

1. 如果JSON对象中有属性是包含双引号的,比如 复制代码 代码如下: { "description": "25"" } 如果转换成字符串形式,将自动加上反斜线,变为"25\"", 然后传递给REST API,保存到MongoDB中. 此时如果使用MongoDB的shell显示该数据,为"25\"", 正确. 2. 但是如果用C++ driver读取这个值是,则得到"25"&

USB3.0接口怎么看,正确识别USB3.0接口技巧

故障现象: USB3.0接口一定是蓝色的吗?   答案是否定的.   USB3.0规范中并没有规定接口必须使用什么颜色,使用蓝色或其他颜色只是为了更直观的和2.0接口作区分,突出产品特点(或卖点).因此不能直接简单地以蓝色接口来区分USB3.0. 解决方案: 联想电脑中,USB3.0除了蓝色的,也可能是红色(或桔红色)或者黑色(和USB2.0一样).   因此,除了颜色以外,我们还需要根据其他更准确的标识特征来正确识别USB3.0.   方法一   按USB3.0规范标识识别.如下图: