ExtJS的store.sync向Asp.net MVC的Action提交时引发System.Reflection.AmbiguousMatchException异常

ExtJs中的store在sync时,是可以批量提交数据的,所谓的批量是指如果在store中同时记录了create、update、delete的对象时,分三次提交。在提交时,多个对象将被组合成JSON格式的字符串,当要提交的数据只有一条时,JSON的字符串如下所示:

{"ID":"a6d671ca-5480-4b5b-bf7a-b8459c0f598b","MobilePIN":"","Email":"111@ee.com","Password":"","CreateDate":null,"LastLoginDate":null,"LastPasswordChangedDate":null,"Comment":"","UserName":"user1","MobileAlias":"","LastActivityDate":null,"DisplayName":"","RememberMe":false}

如果是多条数据时,JSON的字符串是:

[{"ID":"a6d671ca-5480-4b5b-bf7a-b8459c0f598b","MobilePIN":"","Email":"111@ee.com","Password":"","CreateDate":null,"LastLoginDate":null,"LastPasswordChangedDate":null,"Comment":"","UserName":"user1","MobileAlias":"","LastActivityDate":null,"DisplayName":"","RememberMe":false}]

在Controller的Action中,如果方法的签名是public JsonResult CreateUser(UserDataObject user),则当传入多条数据时,只能接收到第1条;当方法签名是private List<UserDataObject> ProcessUsers(List<UserDataObject> users)时,当多条数据时没有问题,可只有1条数据时,users为null值。

我想当然的想到在Action中直接把两个方法都写上,但结果报了System.Reflection.AmbiguousMatchException,即MVC的ActionSelector已经不能区分应该调哪个方法了。

经过查找MVC的代码,发现在AsyncActionMethodSelector的RunSelectionFilters方法中,将会查找在Action上的Atrribute,像HttpPost这样的Attribute都会做过滤,以判断是否符合当前调用的上下文。

知道了这一点以后,我试想可以通过写一个自定义的Attribute,然后根据当前Request请求的内容来决定哪个方法可用,哪个方法不可用,实现这个的关键点有两个:

1、如何实现这个Attribute。

2、如何得到Request的Post的内容并进行解决。

 

第1个问题很好解决,因为我还没有系统的看MVC方面的资料,所以查看一下HttpPost可以知道它继承了ActionMethodSelectorAttribute,这个类里有一个抽象的方法IsValidForRequest。

第2个问题可以得到Request后,然后判断当前Post的类型是否为application/json,进行对Post的数据进行JSON的反序列化。经过测试得知,如果是单一对象的话,反序列化化的对象是一个Dictionay<String,Object>对象,而多个对象得到的是一个Object[]。下面给出实现的源码:

public class JsonModelPostAttribute : ActionMethodSelectorAttribute
    {
        public enum ParameterType
        {
            Single,
            Multiple
        }

        private ParameterType _parameterType;
        public ParameterType MethodParameterType
        {
            get
            {
                return _parameterType;
            }
            set
            {
                _parameterType = value;
            }
        }

        public JsonModelPostAttribute()
        {
            _parameterType = ParameterType.Single;
        }

        public JsonModelPostAttribute(ParameterType type)
        {
            _parameterType = type;
        }

        public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
        {
            var c = GetDeserializedObject(controllerContext);            
            if (c == null)
            {
                return true;
            }
            if (MethodParameterType == ParameterType.Single)
            {
                return c is Dictionary<string, object>;
            }
            else
            {
                return c is object[];
            }
        }

        private static object GetDeserializedObject(ControllerContext controllerContext)
        {
            if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            {
                // not JSON request
                return null;
            }

            controllerContext.HttpContext.Request.InputStream.Seek(0, SeekOrigin.Begin);

            StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
            string bodyText = reader.ReadToEnd();

            controllerContext.HttpContext.Request.InputStream.Seek(0, SeekOrigin.Begin);

            if (String.IsNullOrEmpty(bodyText))
            {
                // no JSON data
                return null;
            }

            JavaScriptSerializer serializer = new JavaScriptSerializer();
            object jsonData = serializer.DeserializeObject(bodyText);
            return jsonData;
        }
    }

需要注意的时,在读取了InputStream后,要重新将Position置为0,否则后续的JsonValueProviderFactory将得不到反序化实例,这样即使能进入到正确的Action,参数也会为空。

下面是Controller中的测试的代码片段:

        [AllowAnonymous]
        [HttpPost]
        [JsonModelPost(JsonModelPostAttribute.ParameterType.Multiple)]
        public JsonResult CreateUser(List<UserDataObject> users)
        {
            ProcessUsers(users);

            return this.Json(new { success = true, user = users }, JsonRequestBehavior.AllowGet);
        }

        private List<UserDataObject> ProcessUsers(List<UserDataObject> users)
        {
            int i = 1;
            foreach (var user in users)
            {
                user.ID = Guid.Empty.ToString();
                user.UserName = "Server Name" + i++;
            }
            return users;
        }

        [AllowAnonymous]
        [HttpPost]
        [JsonModelPost(JsonModelPostAttribute.ParameterType.Single)]
        public JsonResult CreateUser(UserDataObject user)
        {
            var list = new List<UserDataObject>(new UserDataObject[] { user });
            ProcessUsers(list);
            return this.Json(new { success = true, user = list[0] }, JsonRequestBehavior.AllowGet);
        }
posted @ 2013-03-13 16:41  吴东雷  阅读(1940)  评论(0编辑  收藏  举报