c# In ASP.NET MVC, deserializzare JSON prima o nel metodo di azione del controllore




jquery asp.net-mvc (4)

Prova questo;

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Submit(FormCollection collection)
{
    User submittedUser = JsonConvert.DeserializeObject<User>(collection["user"]); 
    return View();
}

Sto lavorando a un sito Web che pubblicherà un oggetto JSON (utilizzando il metodo jQuery Post) sul lato server.

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

Allo stesso tempo, ho scritto classi sul lato server per questa struttura dati.

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; }
}

Quando eseguo il sito Web con il seguente codice nella mia classe controller, la proprietà FullName non viene deserializzata. Che cosa sto facendo di sbagliato?

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Submit(User user)
{
    // At this point, user.FullName is NULL. 

    return View();
}

Dopo alcune ricerche, ho trovato che la soluzione di Takepara era l'opzione migliore per sostituire il deserializer MVC JSON predefinito con Json.NET di Newtonsoft. Può anche essere generalizzato a tutti i tipi in un assembly come segue:

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;
        }
    }
}

Quindi, in Global.asax.cs , Application_Start() :

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

1. Crea un raccoglitore modello personalizzato

  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;
    }
  }

Legante modello 2.add in evento application_start

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

3. Utilizzare jQuery $ .get / $. Post nel codice JavaScript del client di visualizzazione.

  <% using(Html.BeginForm("JsonData","Home",new{},FormMethod.Post, new{id="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>

Ho risolto il mio problema implementando un filtro azione; codice di esempio è fornito di seguito. Dalla ricerca, ho appreso che esiste un'altra soluzione, il modello legante, come Takepara descritto sopra. Ma non so davvero che pro e contro di fare in entrambi gli approcci.

Grazie al post sul blog di Steve Gentile per questa soluzione.

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();
}






json