.NET Web API issues with deserializing JSON to object - asp.net-mvc

I overtook a project which was developed by a company for us, unfortunately we do not get much support and in the long term, we should accomplish maintainence by ourselves for it. The application consists of a simple Web client (HTTP, JavaScript, Knockout Framework) and a REST API Service (.NET 4.5, ASP.NET MVC I guess).
Currently I am only modifiying the client, so the Server should still work as expected.
On the clientside I modified the Knockout View Model a little bit (added some computables and optimized presentation of some values). The View Model consists of 'Issues' and 'Comments' (as an array on issues, its an Bug tracker in fact). If I create a new issue, description is added to a first comment, the whole Model is JSON.stringified and the send to the .NET API Service. I prooved with Firebug, that the JSON that gets posted looks like this:
{
"Id":0,
"Title":"THis is a title",
"Comments":[
{
"Id":1,
"Text":"this is the first comment"
}
]
}
On the client side, I have a "IssueController":
[HttpPost]
public HttpResponseMessage PostIssues( Issue issue ) {
//issue should already hold the deserialized content of the JSON here,
//but it only contains 'Id' and 'Title' not the 'Comments' (empty List)
...
}
The issue domain model object also has an array for holding comments, but on this end its empty already. The .NET code doesn't have any part which explicitely parses the JSON, as far as I understood it, the MVC Framework does this implicitely by equal property names (Is that right?).
The deserialization already worked as expected, so the .NET Code should be fine, but I looks like that I have modified the JSON in a way, that this implicit mapping of comments does not work anymore. Unfortunately I dont have much experiences with the .NET MVC Framework (or is it just the .NET WebAPI Framework, cannot even tell you that).
These are my questions:
What kind of .NET REST API Framework is that? How can I distinguish?
How is this implicit JSON deserialization working, and what are its pitfalls, for example when portions of the JSON doesn't get deserialized as expected? Especially on the client side (as I said, I didn't modify the server code)
Any ideas about what I could have done to the JSON, that the server doesn't like it anymore? ;)
EDIT
Issue.cs looks like this (simplified for sure):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Bo;
using Projects.DomainObjects;
namespace Projects.Models {
public class Issue : DomainObject {
public Issue() {
this.Comments = new List<Comment>();
}
public long Id { get; set; }
private string _Title;
public string Title { get { return _Title; } set { _Title = value; NotifyChanged(); } }
public List<Comment> Comments { get; set; }
}
Comment.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Common;
using Projects.DomainObjects;
namespace Projects.Models {
public class Comment : DomainObject {
public Comment() {
}
public long Id { get; set; }
public string Text { get; set; }
}

I just tried to your code straight from your post and it worked for me.
So there are a few things that may be going wrong on your side:
When you post the object to the server, make sure you are converting back from Knockout observable to a json object. So in your ajax request, make sure it looks like: data: ko.toJSON(issue) and not just data: issue.
When you post the object to the server make sure you sent header content-type: application/json
Here are the answers to your other questions:
What kind of .NET REST API Framework is that? How can I distinguish?
This doesn't look like anything custom (at least, what you posted) it is just straight Web API in .NET.
How is this implicit JSON deserialization working, and what are its pitfalls, for example when portions of the JSON doesn't get deserialized as expected? Especially on the client side (as I said, I didn't modify the server code)
Deserialization on the server uses the collection of formatters that work based on the content-type set by the client. This can get complex if you want to customize it but there is information here
Any ideas about what I could have done to the JSON, that the server doesn't like it anymore? ;)
As I said your code worked for me!

Solved it, the Problem was a computed that returns a sorted array of the comments. So my knockout model contains an
self.Comments = ko.observableArray([]); //...which gets filled with the comments of an issue
and a
self.CommentsSorted = ko.computed(function() {...}); //...which simply returns a sorted Comments array
So when I serialize this model, the posted JSON now represents the Comments Array, but also the CommentsSorted Array. Only when I do
var i = ko.toJS(issue);
delete i.CommentsSorted;
before I post i as data, .NET is able to deserialize Comments correctly.
The mysterious thing about this is, that there were always other computed fields in my knockout model, which get ignored completely by .NET and do not disturb deserialization in any way. So it seems that it depends mainly on the name and type of the fields in the model (perhaps if the first letters are equal?).
The good thing: it works now
The bad thing: it isn't really deterministic how .NET does the deserialization of JSON data and I am also not able to debug if it doesn't behave as expected.

Related

How can I get ASP.NET MVC model binding to bind to just an underscore?

I am using a JS library that passes one of its query string parameters as just an underscore, ex.
SomeUrl/?_ABC123
I tried creating a simple class with an underscore as a property but that doesn't seem to work (nothing appears to be bound).
public class SomeUrlModel
{
public string _ { get; set; }
}
Am I stuck with just creating a custom model binder?
(I could easily just grab this out of the query string but it'd be nice to not have to embed this kind of code everyplace where I can expect this querystring).
Update
Well, apparently my test was incorrect. An underscore IS being properly bound. Something in the JS code is altering the URL between the point where it was set and when I hit my breakpoint in my C#. Strange that I didn't catch this when I first saw it in the network trace in Chrome.

ASP.NET MVC Read Raw JSON Post Data

This is driving me crazy. I'm using ASP.NET MVC. I have a controller with an HttpPost action that acts as a callback URL that is called by another server (not under my control). I want to dynamically read JSON posted to it without using WebAPI or Model Binding. The URL also has a query string parameter passed to it.
The callback URL looks something like this:
http://domain.com/callback?secret=1234
I've tried reading the posted input using:
[HttpPost]
public ActionResult Callback( String secret )
{
String jsonData = new StreamReader(this.Request.InputStream).ReadToEnd();
// ...
}
However "jsonData" is always null or empty.
I just want to get the posted input and stick it into JsonFx so I can dynamically access the contents. Any ideas on how to do this the easiest possible way?
UPDATE
I've discovered the following ...
While the above DOES NOT work (jsonData will be null or empty), the following DOES if I configure what little options I have on the calling server so as to omit the "secret" query string parameter, which is about all I can do on that end since it is not my server. In this case, jsonData will have the correct posted JSON string:
[HttpPost]
public ActionResult Callback( /* String secret */ )
{
String jsonData = new StreamReader(this.Request.InputStream).ReadToEnd();
// ...
}
This is very frustrating to work around and I don't know an easy way to accept both a query string and posted JSON data on a standard MVC controller.
I have a "callback controller" with Action methods that accept various data (via GET, via form POST, via JSON POST, via JSON POST w/ a Query String, etc.) from different third-party servers. These are merchant-type callbacks where I have no control over the formats or methods used to convey information. I just need to accept the callbacks and process the information that should be there.
All of it works fine in my Controller, except the case of "JSON POST w/ a Query String".
This appears (at least to me) to be a shortcoming in standard ASP.NET MVC controllers. ???
Can anyone suggest a solution to this that can be used in a standard ASP.NET MVC controller?
Your initial approach should work if you take into consideration the fact, that ASP.NET MVC model binding has already read the stream, so you should rewind it:
[HttpPost]
public ActionResult Callback(string secret)
{
Request.InputStream.Seek(0, SeekOrigin.Begin);
string jsonData = new StreamReader(Request.InputStream).ReadToEnd();
// ...
}
Reset the position to Zero before reading the stream.
Request.InputStream.Position = 0
For ASP.NET Core 2,this works for me.
[HttpPost]
public ActionResult RawTest() {
using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
{
string content = reader.ReadToEndAsync().Result;
//...
}
//...
}

Returning a list of categories nopCommerce

I am trying to return 2 lists using nopCommerce, Both lists are read-only,
I am looking for the simplest way in doing this as I am writing a metro app and I don't really want to spend weeks learning MVC .
The first list is a list of categories from the base nopCommerce platform , the second list is a list of products.
Both lists need to be returned as JSON, to the calling client.
I have two questions:
Is there a way I can get this list without calling custom code ?
I wrote a plugin using the following code
using System.Collections;
using System.Collections.Generic;
using System.Web.Mvc;
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Services.Catalog;
using Nop.Services.Customers;
using Nop.Core.Plugins;
namespace Nop.Plugin.Other.ONWWW.Controllers
{
public class ONWWWController : Controller
{
public ICategoryService _categoryService;
public ONWWWController(ICategoryService categoryService)
{
this._categoryService = categoryService;
}
public ActionResult Index()
{
return Json(_categoryService.GetAllCategories(), JsonRequestBehavior.AllowGet);
}
}
}
Why, when i run the code, I get the following error ?
No parameterless constructor defined for this object.
Question 1: Your best bet is the Nop.Plugin.Misc.WebServices plugin in the original source code.
Question 2: Did you see that the only constructor you have is
public ONWWWController(ICategoryService categoryService)
Which must accept a parameter? In other words, you have not registered the dependency properly. Try looking at one of the DependencyRegistrar.cs file in any of the default plugin. The Nop.Plugin.Misc.MailChimp plugin, for example, has a DependencyRegistrar.cs that you can refer.
:)

Load Balancing using SQL Server and TempData MVC

I've been told that MVC 1.0 TempData does not work under a load balancer when using SQL Server and that it is because the Dictionary itself is not serializable.
We require this for a project and are looking to be able load balancer effectively.
So I would be very grateful if someone could answer the following questions:
Is there away around this so you can make it work?
Is this fixed in MVC 2.0?
Can we create a ITempDataProvider to fix it?
Or has anyone made a fix to the source code for a project of their own they would like to share?
Cheers,
Jamie
The dictionary itself doesn't need to be serializable. It is what you store inside TempData that needs to be serializable. So for example if you have the following class
[Serializable]
public class Foo
{
public string Bar { get; set; }
}
You can perfectly fine use SQL server for session persistence and write the following code:
TempData["foo"] = new Foo { Bar = "bar" };
Session["foo"] = new Foo { Bar = "bar" };
Mmmm, so any UI model (ASP.Net MVC) would just require the Serializable attribute and that should just work?
How does it work for lists and collection based UI models?

ASP.Net MVC : Sending JSON to Controller

I want to be able to send JSON as opposed to the standard QueryStrings when making a post to my controllers in ASP.Net MVC. I have the Front-End stuff working fine (building and then submitting my JSON objects).
The problem is on the controller side where the default ModelBinders that ship with the MVC framework do not support this.
I have seen a combination of ways around this, one of them is to apply a filter which takes the object as a parameter, uses a JSON library to de-serialise it, and adds that to the action parameters. This is not ideal.
The other, better, way is to use a custom Model Binder. All the ones I have seen though presume you will have only one model and that will be a class rather than a variable. If you have multiple ones it breaks down.
Has anyone else encountered this? One idea I had was if I could simply override how MVC deals with the FormCollection and intercept there, adding the values to the collection myself and hoping MVC can do the rest in it's normal fashion. Does anyone know if that is possible?
The key issue, I think, is that my problem is not with binding because my view models are no different to how they where before. The problem is getting the values from the JSON Post.
If I am correct MVC get's the values from the QueryString and puts it into the form collection which is then used for ModelBinding. So shouldn't the correct method be to change the way the FormCollection gets assigned?
Example of an action:
public ActionResult MyFirstAction(Int32 ID, PersonObject Person, ClassObject ClassDetails)
{
//etc
}
The normal binding works, JSON doesn't and all the example of Model Binders will not work either. My best solution so far is to convert the object to a dictionary and loop though each param and match it up. Doesn't seem ideal.
I use a custom model binder for json like this:
public class JsonModelBinder<T> : IModelBinder {
private string key;
public JsonModelBinder(string requestKey) {
this.key = requestKey;
}
public object BindModel(ControllerContext controllerContext, ...) {
var json = controllerContext.HttpContext.Request[key];
return new JsonSerializer().Deserialize<T>(json);
}
}
And then wire it up in Global.asax.cs like this:
ModelBinders.Binders.Add(
typeof(Product),
new JsonModelBinder<Product>("ProductJson"));
You can read more about this here: Inheritance is Evil: The Epic Fail of the DataAnnotationsModelBinder
EDIT
The JsonModelBinder should be used on the controller action parameter typed as Product only. The Int32 and ClassObject should fall back to the DefaultModelBinder. Are you experiencing a different result?

Resources