Receiving arbitrary JSON object in MVC method - c#

I have a C# view class such as this:
public class DataObject
{
public int Number { get; set; }
public dynamic Data { get; set; } // <-----
}
being used in an MVC method like this
[HttpPost]
public ActionResult SaveData(DataObject request) {}
The problem is that I want to recieve multiple types of objects in the Data property of the DataObject class.
That is, I want both these to work as valid input json objects.
Type 1
{
Number: 1,
Data: {
Text: "a text"
}
}
Type 2
{
Number: 2,
Data: {
Value: 1,
Options: { 1, 2, 3, 4 }
}
}
Is there a way of doing this with either dynamic objects or some other type of json library magic (just making the property dynamic did nothing)?
All i want to do is store this data in a SQL column nvarchar field and return at a later time (through Entity Framework).
An alternate solution would be to create a view model for each type of input but as there will be 100's of variants to it creating all these views and the corresponding input methods would be cumbersome to maintain.
Adding more details as per comment request: The method is called through Angular.
pub.Save = function (jsonData) {
return $http(
{
method: "POST",
url: baseURL + "/Save",
data: { request: jsonData}, // tried this for string
// data: jsonData, // original way
timeout: 30000
}
)
.then(function (result) {
return result.data;
});
}

At the server side, DTO class must match with the same property name which the payload is carrying.
public class DataObject
{
public string test { get; set; } // <-----
}
So, your save method remains the same:
[HttpPost]
public ActionResult SaveData(DataObject request) {}
The payload json is in the object request.test but its seralized.
Deseralize it using Json Library.
How is it handling multiple different types of variables?
Deseralize it to a dynamic type as:
dynamic obj = JsonConvert.DeserializeObject(request.test, typeof(object));
//Properties within the obj are checked at run time.
if(obj.Text != null) {
//Do your thing
}
if(obj.Value != null) {
//Do your thing
}
if(obj.Options != null) {
//Do your thing
}

By converting the data to a JSON string on the client side I was able to send it to the string property and thus being able to use the same typed view for all objects.
I ended up doing this when saving the object (I'm using angular on the front end), converting the Json object to a string.
entry.Data = angular.toJson(entryData.Data, false);
And then when getting the json string back from MVC I did this to get it back to a real javascript object.
entry.Data = angular.fromJson(entry.Data);
MVC would not accept the JSON object into the text property without making it into a json string first.
Using the above method I am storing data like this in my database:
"{\"Value\":123,\"Currency\":\"EUR\"}"

Related

Getting NULL value in ASP .net POST controller method from Axios POST request

I want to store data via POST method using react axios into table via ASP .net. The API works on postman but the data Im sending on the frontend side shows null on the backend POST method.
React code:
var headers = {
'Authorization': 'Bearer ' + localStorage.getItem('tk'),
'Content-Type': 'application/json'
}
axios.post("https://localhost:8000/api/" + 'Author',
{
Data: this.state.books, //"Book1 (string)"
Text: this.state.text, //"Douglas M (string)"
List: [1,2,3] //array
}, {headers: headers})
.then((response) => {
window.location.href = '/home';
})
.catch((error) => {
console.log(error);
});
C# code:
[Produces("application/json")]
[HttpPost]
public HttpResponseMessage Post([FromBody] Author value)
{
Author input = new Author ()
{
Data = value.Data,
Text = value.Text,
Lis = value.List,
Date = Datetime.now
};
_model.Author.Add(input);
_model.SaveChanges();
return new HttpResponseMessage(HttpStatusCode.OK);
}
Author model design:
public class Author
{
public string Data { get; set; }
public string Text { get; set; }
public string Lis { get; set; } //storing list as string in table
}
Error returned:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
When the API is executed, the value that the POST method retrieves is NULL. What's the reason for this? I tried using JSON.stringify() for the data in frontend but it didnt work.
As mentioned by #Rena, the problem lies with your model not being able to parse array sent from client side to a string. You need to change your Lis property to an array so that corresponding properties and their types sent match your model properties and their types.
public class Author
{
public string Data { get; set; }
public string Text { get; set; }
public int[] Lis { get; set; } // <-- change from string to array of numbers (since you're sending an array that contains numbers)
}
Also Lis = value.List will not work with the Database. You should have a model for Client-Server communication and a separate model for your EF Core DbContext. Then do a conversion from one model to the other and vice-versa. Automapper can be usefull too.
Lis = string.Join(", ", value.List)

POST 2 Guid parameters to API with Angular

I Have made an API to follow a user. The method accepts 2 parameters which are two guids. The method:
// Follow user
[HttpPost]
public async Task<ActionResult<Guid>> FollowUser([FromBody] Guid user_gd, Guid user2_gd)
{
if (ModelState.ErrorCount > 0)
{
return BadRequest();
}
var followedUser = await _user.FollowUser(user_gd, user2_gd);
return Ok(followedUser);
}
The manager in API:
public async Task<bool> FollowUser(Guid user_gd, Guid user2_gd)
{
var followUserQuery =
#"
insert into userbind(gd, user_gd, followed_user_gd, date_followed)
values(#_gd, #_user_gd, #_followed_user_gd, #_date_followed)
";
await PostQuery(followUserQuery, new
{
_gd = GenerateGd(),
_user_gd = user_gd,
_followed_user_gd = user2_gd,
_date_followed = DateTime.Now
});
return true;
}
The API request in Angular (service):
followUser(followed_user, user_gd): Observable<any> {
try {
return this._http.post<any>(this._apiUrl + "FollowUser", { "user2_gd": followed_user, "user_gd": user_gd }, this.httpOptions);
} catch (e) {
console.log("POST error: ", e);
}
}
The component:
followUser(gd) {
console.log(gd);
this._userService.followUser(gd, localStorage.getItem("gd")).subscribe(
res => {
console.log(res);
}
)
}
The variables and everything works right now but I am getting this error everytime:\
"Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Guid' because the type requires a JSON primitive value (e.g. string, number, boolean, null) to deserialize correctly.\r\nTo fix this error either change the JSON to a JSON primitive value (e.g. string, number, boolean, null) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.\r\nPath 'user2_gd', line 2, position 15."
Does someone know how to fix this or expierences the same problem? Please reach out.
Try creating class with required fields and accepting it in your FollowUser action as a parameter:
public class FollowUserParams
{
public Guid user_gd { get; set; }
public Guid user2_gd { get; set; }
}
// Follow user
[HttpPost]
public async Task<ActionResult<Guid>> FollowUser([FromBody] FollowUserParams p)
{
... use p
}
Also see Andrew Lock's post on model binding in ASP.

Deserialize JSON into any object

I have an API request that goes off and the response structure back looks like this:
{
"MessageBody": {
"foo" : ""
}
}
The properties under MessageBody can be anything, not only foo, but its value is always a string.
eg. {"MessageBody": { "Token": "abc" }} or {"MessageBody": { "Name": "abc" }}
How can I capture this response from the API as a generic object for the property under MessageBody?
I can represent the first example above as:
public class MessageBody
{
public string Token { get; set; }
}
How would I represent both Token or Name properties under the same MessageBody object? There's a bunch of different values that MessageBody can have, but again they would all be of type string.
I have acheived something similar using Newtonsoft
Your route should take the body in as a generic object and it can then be deserialized into any object you'd like:
/*Using this method, the controller will automatically handle
validating proper Json format*/
[HttpPost]
public async Task<IActionResult> Post([FromBody] object Body)
{
/*here you will send the generic object to a service which will deserialize.
the object into an expected model.*/
customService.HandlePost(Body);
}
Now create an object with any expected fields you would get from the body. (Json2csharp.com is extremely useful!)
public class MessageBody
{
public string Token { get; set; }
public string Name { get; set; }
}
Inside your service you can handle the object like this:
using Newtonsoft.Json
using Models.MessageBody
public class customService()
{
public void HandlePost(object body)
{
var DeserializedBody = JsonConvert.DeserializeObject<MessageBody>(body);
//Any Values that were not assigned will be null in the deserialized object
if(DeserializedBody.Name !== null)
{
//do something
}
}
}
This is obviously a very bare bones implementation, error handling will be important to catch any invalid data. Instead of using one object and null fields to get the data you need, I would recommend adding a "subject" route variable (string) that you can use to determine which object to deserialize the body into.
post *api/MessageBody/{Subject}

ViewModel with dynamic elements

I need to receive the next JSON in .NET
"currentData":
{
"Name": {"system": "wfdss", "canWrite": true },
"DiscoveryDateTime": { "system": "wfdss", "canWrite": true },
"Code": { "system": "code", "canWrite": false },
...
}
This elements are dynamics, it doesn't have default elements, so, how can I define a class doing that following next model:
public class currentData
{
//TODO
//<Data Element Name>: {
//data element system: <STRING of system>,
//the last system to update data element canWrite: <Boolean>
//true if requesting system may edit data element (based on ADS), otherwise false. }, ...
public List<Property> property { get; set; }
}
public class Property
{
public string system { get; set; }
public string canWrite { get; set; }
}
If you need to post dynamic structured Json to controller i have a bad news for you - you can't map it automattically in MVC. MVC model binding mechanism work only with stronly typed collecions - you must know structure.
One of the options that i can suggest you if use FormCollection and manually get values from it:
[HttpPost]
public JsonResult JsonAction(FormCollection collection)
{
string CurrentDataNameSystem = collection["currentData.Name.system"];
// and so on...
return Json(null);
}
Another option is to pass you dynamic json as string and then manually desirialize it:
[HttpPost]
public JsonResult JsonAction(string json)
{
//You probably want to try desirialize it to many different types you can wrap it with try catch
Newtonsoft.Json.JsonConvert.DeserializeObject<YourObjectType>(jsonString);
return Json(null);
}
Anyway my point is - you shouldn't mess with dynamic json unless you really need it in MVC.
I suggest you to creage object type that contain all the passible fields but make it all nullable so you can pass your Json and it will be mapped with Model binding MVC mechanism but some fields will be null.
I think the type format you are getting is an Object with a Dictionary.
So i think you need to Deserialize your Data into this.
public class ContainerObject
{
public Dictionary<String,Property> currentData { get; set; }
}

Can I manually hard code a JSON object to be returned by ASP.NET web API?

I'm used to doing this in Django (similar to Ruby on Rails) where in some cases I need to hard code a JSON response object for the client to be able to interpret, but I've been searching everywhere online on figuring out how to do this with ASP.NET web API and I can't find anything on this, ASP.NET web API seems to be forcing me to create a class to represent a JSON response for every URI controller.
For example, here's the only way I know for manually creating a JSON response:
1.) I first need to create the class to represent the response object
public class XYZ_JSON
{
public string PropertyName { get; set; }
public string PropertyValue { get; set; }
}
2.) Then I need to properly write up the URI controller that'll return an "XYZ_JSON" that I've just defined above:
// GET: api/ReturnJSON
public XYZ_JSON Get()
{
XYZ_JSON test = new XYZ_JSON { PropertyName = "Romulus", PropertyValue = "123123" };
return test;
}
Will result with an http response of something like:
200 OK
{"PropertyName":"Romulus", "PropertyValue":"123123"}
This whole class to JSON design pattern is cool and all, but it's not helpful and actually makes things much worse when trying to return a class as a JSON object with many classes within it such as:
public class XYZ_JSON
{
public string PropertyName { get; set; }
public string PropertyValue { get; set; }
public List<ComplexObject> objects { get; set; } // <- do not want
}
The JSON response object above isn't that complex, but for what I'm trying to accomplish I'll have to put a list of classes within a list of classes within a list of classes, and I can't develop it in this awkward way unless I spend a week on it which is just ridiculous.
I need to be able to return a JSON response in this kind of fashion:
// GET: api/ReturnJSON
public JSON_Response Get(string id)
{
// do some SQL querying here to grab the model or what have you.
if (somethingGoesWrong = true)
return {"result":"fail"}
else
return {"result":"success","value":"some value goes here"}
}
The design pattern above is what I'm trying to accomplish with ASP.NET web API, a very simply way to return a semi-hard coded JSON response object which would allow me to return very unique and dynamic responses from a single URI. There's going to be many use cases where a list of up to 8 completely unique Class objects will be returned.
Also, If what I'm trying to accomplish is the backwards way of doing things than that's fine. I've released a very successful and stable iOS application with a flawless Django backend server handling things this way perfectly without any issues.
Can someone explain to me how I can return a simple hard coded JSON response using the ASP.NET web API?
Thanks!
You can create anonymous types in C#, so you can use one of these to produce your hard-coded result. For example:
return new JsonResult
{
Data = new
{
result = "success",
value = "some value"
}
};
To clarify, the above code is for ASP.NET MVC. If you're using Web API, then you can just return the data object, or use an IHttpActionResult. The anonymous type part (the new {}) stays the same.
Use an anonymous object.
public object Get(string id)
{
// do some SQL querying here to grab the model or what have you.
if (somethingGoesWrong = true)
return new {result = "fail"}
else
return new {result = "success", value= "some value goes here"}
}
You can use a generic JObject to return your values without constructing a complete class structure as shown below
public JObject Get(int id)
{
return JsonConvert.DeserializeObject<JObject>(#"{""result"":""success"",""value"":""some value goes here""}");
}
For hard coded response, why not just do something like below. The JSON content will be returned without being surrounded by quotation marks.
public HttpResponseMessage Get()
{
string content = "Your JSON content";
return BuildResponseWithoutQuotationMarks(content);
}
private HttpResponseMessage BuildResponseWithoutQuotationMarks(string content)
{
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(content);
return response;
}
private HttpResponseMessage BuildResponseWithQuotationMarks(string content)
{
var response = Request.CreateResponse(HttpStatusCode.OK, content);
return response;
}
// GET: api/ReturnJSON
public JsonResult Get()
{
return Json(new { Property1 = "Value1", Property2 = "Value2" });
}
You can return json using JsonResult class. and the Json() method takes anonymous object so you don't need to create a class.

Categories