当前位置: 首页 > 科技观察

MVC3无法正确识别JSON中的Enum枚举值

时间:2023-03-13 07:19:43 科技观察

一、背景在MVC3项目中,如果Action参数中有一个Enum枚举作为对象属性,那么使用POST方式提交的JSON数据中的枚举值就是对应的枚举无法正确识别值。2.Demo演示为了说明问题,我使用MVC3工程创建了一个Controller,并创建了如下代码演示://TrafficmodeenumerationpublicenumTrafficEnum{Bus=0,Boat=1,Bike=2,}publicclassPerson{publicintID{获取;设置;}publicTrafficEnumTraffic{get;set;}}publicclassDemoController:Controller{publicActionResultIndex(Personp){returnView();}}网站生成成功后,就可以使用Fiddler发送HTTPPOST请求了。注意需要在requestheader中添加Addcontent-type:application/json,以通知服务端RequestBody中的内容为JSON格式。点击右上角的执行,执行HTTP请求。在程序断点的情况下,检查参数p。属性ID已经被正确识别为9999,但是枚举值属性Traffic被误认为是枚举中的第一个值Bus,这似乎是错误的,即使将Traffic改为Bike,即值等于2,结果也是一样的。3、解决方法一:升级MVC4,本人在MVC4项目下测试,此问题已修复;方法二:如果由于各种原因,项目不想或者不能升级到MVC4,可以在MVC3项目中做一些改动,也可以解决这个问题。1.在项目中,新建一个类,添加如下代码。需要参考usingSystem.ComponentModel;使用System.Web.Mvc;命名空间;///

///MVC3下的处理,提交的JSON枚举值在Controller不能识别的问题///publicclassEnumConverterModelBinder:DefaultModelBinder{。财产种类;如果(propertyType.IsEnum){varproviderValue=bindingContext.ValueProvider.GetValue(bindingContext.ModelName);if(null!=providerValue){varvalue=providerValue.RawValue;if(null!=value){varvalueType=value.GetType();如果(!valueType.IsEnum){returnEnum。ToObject(属性类型,值);}}}}returnbase.GetPropertyValue(controllerContext,bindingContext,propertyDescriptor,propertyBinder);}}2。在Global.asax的Application_Start方法中,输入行EnumConverterModelBinder类的实例化操作:protectedvoidApplication_Start(){//MVC3下,提交的JSON枚举值无法被Controller识别ModelBinders.Binders.DefaultBinder=newEnumConverterModelBinder();}配置改造后,我重新生成Website,重新发送HTTP请求看,MVCAction中参数中的枚举可以正确识别#p#四、研究我觉得这应该是mvc3的一个小缺陷,与mvc的升级,这个在新版本中已经完美修复,但是如果还在使用mvc3的人在项目中遇到这个问题,可以研究一下。遇到问题,去百度、谷歌找解决办法是可以的,但是复制粘贴代码后,最好问问自己为什么这样可以解决问题。从现象和解决代码猜测,应该是MVC生命周期的ModelBinders部分出了问题。因为MVC已经开源了,所以我尝试调试源码,先下载MVC3的源码,其他项可以去掉,只保留红框中的项,然后新建一个MVC3测试工程,系统.去掉了web.mvc的引用,取而代之的是引用本方案中的system.web.mvc项目,方便我们调试MVC源码。从搜索到的代码可以看出,我们的自定义类继承了DefaultModelBinder父类,重写了GetPropertyValue方法。那么我们从这点出发,在MVC3源码中的System.Web.MVC项目中找到这个类。这里在方法上插入一个断点。F5调试器,发送POST请求。实际上,BindProperty方法会被执行多次。BindProperties方法会遍历请求的实体类的属性,每个属性都要经过BindProperty方法处理;现在第一个属性ID已被拦截。紧接着,程序进入propertyBinder.BindModel方法。只贴了一部分关键代码,通过bindingContext的ValueProvider获取属性的相关信息。如果不等于null,就去执行BindSimpleModel方法。#p#在BindSimpleModel方法中,首先使用Type.IsInstanceOfType方法判断指定对象是否为当前Type的实例,如果是则直接返回rawValue,其中属性类型为Int32类型,返回True满足条件,所以直接把rawValue返回。第一个Int32类型属性的部分关键代码已经执行,这里的值已经确定。接下来,我们可以看到有问题的Enum枚举类型属性。循环到了第二个属性。这时,我注意到有一个Model属性。对比Int32类型执行的时候,这个属性当时是0,但是此时是Bus。可以看出这是一个默认值。在指定枚举中值为0的类型(即使你没有显式地为枚举指定一个值),同样,通过BindModel方法到BindSimpleModel方法。此时对比Int32类型的属性ID,此时ModelType.IsInstanceOfType(valueProvideResult.RawValue)为False,如果不是字符串类型则进行下面的判断,不是数组类型,所以我们来到最后一个,根据绿色的注释可以看出,这应该是一个判断集合类型是否为集合类型的方法,而Enum不是,所以返回Null。此时TypecollectionType变量为Null,执行最后一个case3。在ConvertProviderResult方法中,也进行了一系列的类型判断转换。目的是将JSON中的数值类型转换为枚举值,但是执行过程中抛出异常。原因是“Notypeconvertercanconvertbetweenthesetypes”也就是说,在MVC3机制中,没有对应的类型转换器来处理值与枚举的对应关系。经过以上处理方式后,对应的数值尚未确定。如何与原来的BindProperty老板方法有所不同。所以小的只好如实带回Value=Null和modelState.Errors模型错误状态信息,让老大来决定怎么做,老大后面的处理有点乱,不过我觉得源码估计是用默认值作为Value,所以json传过来的值是不对应对应枚举的值的,无论传什么值,结果总是第一个枚举的值。5.小结本文只是自己在工作中遇到的一个小问题,后来有点兴趣从源码的角度去研究分析。缺乏理论基础,因为之前没有深入研究过MVC的底层运行机制。还有生命周期,所以这方面还需要进一步研究。如果你也有兴趣,可以下载我修改后的源码进行分析,甚至可以下载MVC4的源码进行对比。