在JsonValueProviderFactory中修改ModelState

时间:2012-07-19 12:19:55

标签: json asp.net-mvc-3

在JsonValueProviderFactory的默认实现中,使用JavaScriptSerializer.DeserializeObject()方法。如果json字符串格式错误,则此方法抛出异常。然后服务器向浏览器抛出500黄页。我想通过控制器操作来抑制异常并显示自定义错误消息。

这就是我尝试过的。我删除了默认的JsonValueProviderFactory并引入了一个自定义的JsonValueProviderFactory,其代码基本相同。唯一的区别在于我引入了try-catch块的GetDeserializedObject方法。

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

        StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
        string bodyText = reader.ReadToEnd();
        if (String.IsNullOrEmpty(bodyText))
        {
            // no JSON data
            return null;
        }

        JavaScriptSerializer serializer = new JavaScriptSerializer();
        object jsonData = null;
        try
        {
            jsonData = serializer.DeserializeObject(bodyText);
        }
        catch (Exception ex) {
            ex.Data["jsonString"] = bodyText;
            throw;
        }
        return jsonData;
    }

我想将异常错误消息传递给modelbinder并最终在modelstate字典中设置,而不是简单地抛出异常。然后我可以在控制器动作中读取模型状态字典并自定义发送到浏览器的错误消息。可以这样做吗? 如果无法做到,是否有其他方法可以实现相同的目标?

2 个答案:

答案 0 :(得分:0)

我没试过,但你可以试试,

controllerContext.ParentActionViewContext.ViewData.ModelState.AddModelError

答案 1 :(得分:0)

我终于找到了正确的方法。我正在记录它以供其他人参考。

controllerContext.Controller.ViewData.ModelState.AddModelError("improper json", ex)

这正是如何设置modelbinder的modelbindingContext中的模型状态。我从MVC3源复制相关的代码片段。该方法存在于ControllerActionInvoker.cs中。

 protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) {
        // collect all of the necessary binding properties
        Type parameterType = parameterDescriptor.ParameterType;
        IModelBinder binder = GetModelBinder(parameterDescriptor);
        IValueProvider valueProvider = controllerContext.Controller.ValueProvider;
        string parameterName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;
        Predicate<string> propertyFilter = GetPropertyFilter(parameterDescriptor);

        // finally, call into the binder
        ModelBindingContext bindingContext = new ModelBindingContext() {
            FallbackToEmptyPrefix = (parameterDescriptor.BindingInfo.Prefix == null), // only fall back if prefix not specified
            ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType),
            ModelName = parameterName,
            ModelState = controllerContext.Controller.ViewData.ModelState,
            PropertyFilter = propertyFilter,
            ValueProvider = valueProvider
        };

        object result = binder.BindModel(controllerContext, bindingContext);
        return result ?? parameterDescriptor.DefaultValue;
    }