在 ASP.NET MVC 中,在控制器的操作方法之前或之中反序列化 JSON

Posted

技术标签:

【中文标题】在 ASP.NET MVC 中,在控制器的操作方法之前或之中反序列化 JSON【英文标题】:In ASP.NET MVC, deserialize JSON prior to or in controller's action method 【发布时间】:2010-12-01 06:52:57 【问题描述】:

我正在开发一个将 JSON 对象(使用 jQuery Post 方法)发布到服务器端的网站。

 
    "ID" : 1,
    "FullName" : 
       "FirstName" : "John",
       "LastName" : "Smith"
    

同时,我在服务端为这个数据结构写了类。

public class User

    public int ID  get; set; 
    public Name FullName  get; set;


public class Name

    public string FirstName  get; set; 
    public string LastName  get; set; 

当我在控制器类中使用以下代码运行网站时,FullName 属性不会被反序列化。我究竟做错了什么?

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Submit(User user)

    // At this point, user.FullName is NULL. 

    return View();

【问题讨论】:

MVC 不支持开箱即用的 JSON 反序列化,但我们正在考虑为 v2.0 添加它。同时,您可以使用 javascriptSerializer 将请求正文转换为完全水合的 User 对象。 @Levi - 这应该是一个回答帖子;) 这很奇怪;不知何故,ID 属性被正确反序列化。如果我使用 JavaScriptSerializer,Submit() 的输入参数会是 Object 类型吗? 我只是使用 jQuery 表单库,它只是像普通表单一样发布它。如果你不能这样做,我会使用自定义模型绑定器。 【参考方案1】:

我通过实施动作过滤器解决了我的问题;下面提供了代码示例。从研究中,我了解到还有另一种解决方案,即模型绑定器,如上述 takepara 所述。但我真的不知道这两种方法的利弊。

感谢 Steve Gentile 的 blog post 提供此解决方案。

public class JsonFilter : ActionFilterAttribute
    
        public string Parameter  get; set; 
        public Type JsonDataType  get; set; 

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        
            if (filterContext.HttpContext.Request.ContentType.Contains("application/json"))
            
                string inputContent;
                using (var sr = new StreamReader(filterContext.HttpContext.Request.InputStream))
                
                    inputContent = sr.ReadToEnd();
                

                var result = JsonConvert.DeserializeObject(inputContent, JsonDataType);
                filterContext.ActionParameters[Parameter] = result;
            
        
    

[AcceptVerbs(HttpVerbs.Post)]
[JsonFilter(Parameter="user", JsonDataType=typeof(User))]
public ActionResult Submit(User user)

    // user object is deserialized properly prior to execution of Submit() function

    return View();

【讨论】:

awww snap,133t MVC 技能的weilin,你最好在星期一教我; ) 这个方法比自定义 ModelBinder 有一个显着的优势,你可以定义要反序列化的类型。使用自定义 ModelBinder,它是硬编码的,因此仅对一种类型有用。【参考方案2】:

1.创建自定义模型绑定器

  public class UserModelBinder : IModelBinder
  
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    
      User model;

      if(controllerContext.RequestContext.HttpContext.Request.AcceptTypes.Contains("application/json"))
      
        var serializer = new JavaScriptSerializer();
        var form = controllerContext.RequestContext.HttpContext.Request.Form.ToString();
        model = serializer.Deserialize<User>(HttpUtility.UrlDecode(form));
      
      else
      
        model = (User)ModelBinders.Binders.DefaultBinder.BindModel(controllerContext, bindingContext);
      

      return model;
    
  

2.在application_start事件中添加模型绑定

  ModelBinders.Binders[typeof(User)] = new UserModelBinder();

3.在查看客户端 JavaScript 代码中使用 jQuery $.get/$.post。

  <% using(html.BeginForm("JsonData","Home",new,FormMethod.Post, newid="jsonform"))  %>

    <% = Html.TextArea("jsonarea","",new id="jsonarea") %><br />

    <input type="button" id="getjson" value="Get Json" />
    <input type="button" id="postjson" value="Post Json" />
  <%  %>
  <script type="text/javascript">
    $(function() 
      $('#getjson').click(function() 
        $.get($('#jsonform').attr('action'), function(data) 
          $('#jsonarea').val(data);
        );
      );

      $('#postjson').click(function() 
        $.post($('#jsonform').attr('action'), $('#jsonarea').val(), function(data) 
          alert("posted!");
        ,"json");
      );
    );
  </script>

【讨论】:

你的意思可能是“ContentType”而不是“AcceptTypes”【参考方案3】:

你可以试试Json.NET。 documentation 还不错,应该可以do what you need。您还需要获取JsonNetResult,因为它返回一个可在 ASP.NET MVC 应用程序中使用的 ActionResult。它非常易于使用。

Json.NET 也适用于日期序列化。有关 can be found here 的更多信息。

希望这会有所帮助。

【讨论】:

【参考方案4】:

试试这个;

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Submit(FormCollection collection)

    User submittedUser = JsonConvert.DeserializeObject<User>(collection["user"]); 
    return View();

【讨论】:

【参考方案5】:

经过一番研究,我发现 Takepara 的解决方案是用 Newtonsoft 的 Json.NET 替换默认 MVC JSON 反序列化器的最佳选择。它也可以推广到程序集中的所有类型,如下所示:

using Newtonsoft.Json;

namespace MySite.Web

    public class MyModelBinder : IModelBinder
    
        // make a new Json serializer
        protected static JsonSerializer jsonSerializer = null;

        static MyModelBinder()
        
            JsonSerializerSettings settings = new JsonSerializerSettings();
            // Set custom serialization settings.
            settings.DateTimeZoneHandling= DateTimeZoneHandling.Utc;
            jsonSerializer = JsonSerializer.Create(settings);
        

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        
            object model;

            if (bindingContext.ModelType.Assembly == "MyDtoAssembly")
            
                var s = controllerContext.RequestContext.HttpContext.Request.InputStream;
                s.Seek(0, SeekOrigin.Begin);
                using (var sw = new StreamReader(s))
                
                    model = jsonSerializer.Deserialize(sw, bindingContext.ModelType);
                
            
            else
            
                model = ModelBinders.Binders.DefaultBinder.BindModel(controllerContext, bindingContext);
            
            return model;
        
    

那么,在Global.asax.csApplication_Start()

        var asmDto = typeof(SomeDto).Assembly;
        foreach (var t in asmDto.GetTypes())
        
            ModelBinders.Binders[t] = new MyModelBinder();
        

【讨论】:

以上是关于在 ASP.NET MVC 中,在控制器的操作方法之前或之中反序列化 JSON的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ASP.NET MVC 中使用 jQuery ajax 方法将数据列表发送到控制器操作方法?

在 ASP.NET MVC 中,在控制器的操作方法之前或之中反序列化 JSON

使用 asp.net MVC 中的参数将 javascript 中的页面重新加载到特定控制器的操作方法

ASP.NET Core MVC 之控制器(Controller)

如何在 ASP.NET MVC 中执行辅助操作(即计算字段)?

AJAX 发布到 ASP.NET MVC 6 控制器操作方法和参数为空