Spring Boot Post API returns null values - javascript

I've been playing around with Spring MVC and came across an issue, where I send a JSON object from the frontend with JavaScript to the backend. To do so, I use a POST Method, which seems to return null values on the backend.
JavaScript:
function calcBudget() {
var table = document.getElementById("mainTable");
const jsonBody = [];
for (var i = 1; i < table.rows.length; i++) {
const jsonItem = {};
jsonItem ["name"] = table.rows[i].cells[0].firstChild.value;
jsonItem ["category"] = table.rows[i].cells[1].firstChild.value;
jsonItem ["amount"] = table.rows[i].cells[2].firstChild.value;
jsonBody.push(jsonItem);
}
console.log(JSON.stringify(jsonBody));
var xhr = new XMLHttpRequest();
xhr.open("POST", "/home/test", true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
jsonBody
}));
}
JavaScript Output:
[{"name":"01","category":"01","amount":"03"}]
RestController:
#Controller
public class GreetingsController {
#PostMapping("/home/test")
public String readInput(#RequestBody CalcModel model) {
System.out.println(model.expenseName +" " + model.expenseCategory+" "+ model.expenseAmount);
return "index";
}
}
RestController Output:
null null null
CalcModel:
public class CalcModel {
public String expenseName;
public String expenseCategory;
public Double expenseAmount;
public CalcModel(String expenseName, String expenseCategory, Double expenseAmount) {
this.expenseName = expenseName;
this.expenseCategory = expenseCategory;
this.expenseAmount = expenseAmount;
}
}
Why is the RestController output null? I have attempted to integrate an H2 database, as I assumed I would need one. Turned out, though, that this does not work either. I also tried to use getters for the CalcModel, without any luck. Any ideas?
Edit:
It seems as I'm sending a JSON array to Spring Boot, which expects to find just a single object, not an array of objects. Removing the square brackets seems to solve the issue when testing via Postman. Now I need to find a solution how to make SpringBoot recognize the response body as a JSON array.
Solution:
Change RestController Method to this (Request Body needs to be an ArrayList)
#PostMapping("/home/test")
public String readInput(#RequestBody ArrayList<CalcModel> model) {
for (CalcModel calcModel : model) {
System.out.println(calcModel.name + " " + calcModel.category + " " + calcModel.amount);
}
return "index";
}
In the JavaScript file, remove the curly brackets
xhr.send(JSON.stringify(jsonBody));

It seems like you have different namings in the jsonBody and the CalcModel. Try changing the names to the same thing.
[{"name":"01","category":"01","amount":"03"}]
public class CalcModel {
public String name;
public String category;
public Double amount;
You might also need a parameterless constructor and getters and setters.

Related

Passing string from Angular to API

I want to fill in one string parameter in my ASP.NET Core MVC API controller via Angular.
I have this working call:
API
//A class to wrap my string
public class Foo
{
public string bar { get; set; }
}
[HttpPost]
public JsonResult GetDetails([FromBody] Foo bar) { ... }
Angular (service)
public get() {
let bar: any = new Object();
bar.foo = 'Hello World';
return this._httpClient.post('api/GetDetails', bar).toPromise();
}
But what I really want is pass a string without having to wrap it in a class like this:
API
[HttpPost]
public JsonResult GetDetails([FromBody] string bar) { ... }
Angular (service)
public get() {
let bar: string = "Hello World";
return this._httpClient.post('api/GetDetails', bar).toPromise();
}
But I get errors like it's an Unsupported Media Type or 'bar' stays null.
What is the right syntax to just pass one value?
The other thing is, I can pass an integer from Angular to the API just fine but not a string.
Try this :
let bar = JSON.stringify({'foo' : 'Hello World'});
let body = new HttpParams();
body = body.set('bar', bar);
http.post('/api/GetDetails', body).subscribe();
2 ways, depending on whether you have control over back or front end.
1. Angular-service
Use header application/json; charset=utf-8 as described here and here (note the charset)
2. API
The other one is to build a custom string-binder derived which spits out a string.
[HttpPost]
public JsonResult GetDetails([ModelBinder(typeof(StringBinder))] string bar) { ... }
where
public class StringBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
using (var sr = new StreamReader(bindingContext.HttpContext.Request.Body))
Body = sr.ReadToEnd();
bindingContext.Result = ModelBindingResult.Success(Model);
return Task.CompletedTask;
}
}
In Agnular 10 the following works:
let bar = 'Hello World!';
this._httpClient.post('api/GetDetails', '=' + bar, {
headers: new HttpHeaders({
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
})
})
PS: the way I figured it out was by comparing curls generated from dev tools for the working request in AngularJs and not working request in Angular 10

Create JSON Object Using ASP.NET [duplicate]

I just used the XmlWriter to create some XML to send back in an HTTP response. How would you create a JSON string. I assume you would just use a stringbuilder to build the JSON string and them format your response as JSON?
Using Newtonsoft.Json makes it really easier:
Product product = new Product();
product.Name = "Apple";
product.Expiry = new DateTime(2008, 12, 28);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };
string json = JsonConvert.SerializeObject(product);
Documentation: Serializing and Deserializing JSON
You could use the JavaScriptSerializer class, check this article to build an useful extension method.
Code from article:
namespace ExtensionMethods
{
public static class JSONHelper
{
public static string ToJSON(this object obj)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(obj);
}
public static string ToJSON(this object obj, int recursionDepth)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RecursionLimit = recursionDepth;
return serializer.Serialize(obj);
}
}
}
Usage:
using ExtensionMethods;
...
List<Person> people = new List<Person>{
new Person{ID = 1, FirstName = "Scott", LastName = "Gurthie"},
new Person{ID = 2, FirstName = "Bill", LastName = "Gates"}
};
string jsonString = people.ToJSON();
Simlpe use of Newtonsoft.Json and Newtonsoft.Json.Linq libraries.
//Create my object
var myData = new
{
Host = #"sftp.myhost.gr",
UserName = "my_username",
Password = "my_password",
SourceDir = "/export/zip/mypath/",
FileName = "my_file.zip"
};
//Tranform it to Json object
string jsonData = JsonConvert.SerializeObject(myData);
//Print the Json object
Console.WriteLine(jsonData);
//Parse the json object
JObject jsonObject = JObject.Parse(jsonData);
//Print the parsed Json object
Console.WriteLine((string)jsonObject["Host"]);
Console.WriteLine((string)jsonObject["UserName"]);
Console.WriteLine((string)jsonObject["Password"]);
Console.WriteLine((string)jsonObject["SourceDir"]);
Console.WriteLine((string)jsonObject["FileName"]);
This library is very good for JSON from C#
http://james.newtonking.com/pages/json-net.aspx
This code snippet uses the DataContractJsonSerializer from System.Runtime.Serialization.Json in .NET 3.5.
public static string ToJson<T>(/* this */ T value, Encoding encoding)
{
var serializer = new DataContractJsonSerializer(typeof(T));
using (var stream = new MemoryStream())
{
using (var writer = JsonReaderWriterFactory.CreateJsonWriter(stream, encoding))
{
serializer.WriteObject(writer, value);
}
return encoding.GetString(stream.ToArray());
}
}
If you need complex result (embedded) create your own structure:
class templateRequest
{
public String[] registration_ids;
public Data data;
public class Data
{
public String message;
public String tickerText;
public String contentTitle;
public Data(String message, String tickerText, string contentTitle)
{
this.message = message;
this.tickerText = tickerText;
this.contentTitle = contentTitle;
}
};
}
and then you can obtain JSON string with calling
List<String> ids = new List<string>() { "id1", "id2" };
templateRequest request = new templeteRequest();
request.registration_ids = ids.ToArray();
request.data = new templateRequest.Data("Your message", "Your ticker", "Your content");
string json = new JavaScriptSerializer().Serialize(request);
The result will be like this:
json = "{\"registration_ids\":[\"id1\",\"id2\"],\"data\":{\"message\":\"Your message\",\"tickerText\":\"Your ticket\",\"contentTitle\":\"Your content\"}}"
Hope it helps!
You can also try my ServiceStack JsonSerializer it's the fastest .NET JSON serializer at the moment. It supports serializing DataContracts, any POCO Type, Interfaces, Late-bound objects including anonymous types, etc.
Basic Example
var customer = new Customer { Name="Joe Bloggs", Age=31 };
var json = JsonSerializer.SerializeToString(customer);
var fromJson = JsonSerializer.DeserializeFromString<Customer>(json);
Note: Only use Microsofts JavaScriptSerializer if performance is not important to you as I've had to leave it out of my benchmarks since its up to 40x-100x slower than the other JSON serializers.
Take a look at http://www.codeplex.com/json/ for the json-net.aspx project. Why re-invent the wheel?
If you want to avoid creating a class and create JSON then Create a dynamic Object and Serialize Object.
dynamic data = new ExpandoObject();
data.name = "kushal";
data.isActive = true;
// convert to JSON
string json = Newtonsoft.Json.JsonConvert.SerializeObject(data);
Read the JSON and deserialize like this:
// convert back to Object
dynamic output = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
// read a particular value:
output.name.Value
ExpandoObject is from System.Dynamic namespace.
If you can't or don't want to use the two built-in JSON serializers (JavaScriptSerializer and DataContractJsonSerializer) you can try the JsonExSerializer library - I use it in a number of projects and works quite well.
If you're trying to create a web service to serve data over JSON to a web page, consider using the ASP.NET Ajax toolkit:
http://www.asp.net/learn/ajax/tutorial-05-cs.aspx
It will automatically convert your objects served over a webservice to json, and create the proxy class that you can use to connect to it.
Encode Usage
Simple object to JSON Array EncodeJsObjectArray()
public class dummyObject
{
public string fake { get; set; }
public int id { get; set; }
public dummyObject()
{
fake = "dummy";
id = 5;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append('[');
sb.Append(id);
sb.Append(',');
sb.Append(JSONEncoders.EncodeJsString(fake));
sb.Append(']');
return sb.ToString();
}
}
dummyObject[] dummys = new dummyObject[2];
dummys[0] = new dummyObject();
dummys[1] = new dummyObject();
dummys[0].fake = "mike";
dummys[0].id = 29;
string result = JSONEncoders.EncodeJsObjectArray(dummys);
Result:
[[29,"mike"],[5,"dummy"]]
Pretty Usage
Pretty print JSON Array PrettyPrintJson() string extension method
string input = "[14,4,[14,\"data\"],[[5,\"10.186.122.15\"],[6,\"10.186.122.16\"]]]";
string result = input.PrettyPrintJson();
Results is:
[
14,
4,
[
14,
"data"
],
[
[
5,
"10.186.122.15"
],
[
6,
"10.186.122.16"
]
]
]
The DataContractJSONSerializer will do everything for you with the same easy as the XMLSerializer. Its trivial to use this in a web app. If you are using WCF, you can specify its use with an attribute. The DataContractSerializer family is also very fast.
I've found that you don't need the serializer at all. If you return the object as a List.
Let me use an example.
In our asmx we get the data using the variable we passed along
// return data
[WebMethod(CacheDuration = 180)]
public List<latlon> GetData(int id)
{
var data = from p in db.property
where p.id == id
select new latlon
{
lat = p.lat,
lon = p.lon
};
return data.ToList();
}
public class latlon
{
public string lat { get; set; }
public string lon { get; set; }
}
Then using jquery we access the service, passing along that variable.
// get latlon
function getlatlon(propertyid) {
var mydata;
$.ajax({
url: "getData.asmx/GetLatLon",
type: "POST",
data: "{'id': '" + propertyid + "'}",
async: false,
contentType: "application/json;",
dataType: "json",
success: function (data, textStatus, jqXHR) { //
mydata = data;
},
error: function (xmlHttpRequest, textStatus, errorThrown) {
console.log(xmlHttpRequest.responseText);
console.log(textStatus);
console.log(errorThrown);
}
});
return mydata;
}
// call the function with your data
latlondata = getlatlon(id);
And we get our response.
{"d":[{"__type":"MapData+latlon","lat":"40.7031420","lon":"-80.6047970}]}
Include:
using System.Text.Json;
Then serialize your object_to_serialize like this:
JsonSerializer.Serialize(object_to_serialize)

How read data sent by Client with Spark?

I have to read some data sent by Client using Spark (a framework for Java).
This is the code of client's post request. I am using jQuery.
$.post("/insertElement",
{item:item.value, value: value.value, dimension: dimension.value });
The code of server:
post(new Route("/insertElement") {
#Override
public Object handle(Request request, Response response) {
String item = (String) request.attribute("item");
String value = (String) request.attribute("value");
String dimension = (String) request.attribute("dimension");
Element e = new Element(item, value, dimension);
ElementDAO edao = new ElementDAO();
edao.insert(e);
JSONObject json = JSONObject.fromObject( e );
return json;
}
});
I am using Spark so I only have to define the route.
I would like to store in a database the data sent by client, but all the attributes are null.
I think that this way isn't correct. How can I read the sent data?
They way you send your data, using HTTP POST, you're posting the JSON as request body, not as request attributes. This means you shouldn't use request.attribute("item") and the others, but instead parse the request body to a Java object. You can use that object to create the element and store it using the DAO.
You will need something like this:
post(new Route("/insertElement") {
#Override
public Object handle(Request request, Response response) {
String body = request.body();
Element element = fromJson(body, Element.class);
ElementDAO edao = new ElementDAO();
edao.insert(e);
JSONObject json = JSONObject.fromObject( e );
return json;
}
});
public class Element {
private String item;
private String value;
private String dimension;
//constructor, getters and setters
}
public class JsonTransformer {
public static String toJson(Object object) {
return new Gson().toJson(object);
}
public static <T extends Object> T fromJson(String json, Class<T> classe) {
return new Gson().fromJson(json, classe);
}
}
try using request.queryParams("item") and so on.
Assuming this is the JSON I'm sending in my request
{ 'name': 'Rango' }
This is how I've configured my Controller to parse request body.
public class GreetingsController {
GreetingsController() {
post("/hello", ((req, res) -> {
Map<String, String> map = JsonUtil.parse(req.body());
return "Hello " + map.get("name") + "!";
})));
}
}
public class JsonUtil {
public static Map<String, String> parse(String object) {
return new Gson().fromJson(object, Map.class);
}

Pass List<int> from Javascript to AJAX-Enabled WCF in ASP.Net page

I am calling a WCF method from an ASP.Net page, but I am getting format exception when WCF tries to deserialize the recordIds parameter received from JavaScript.
The first parameter passed to the WCF method needs to be of List type. Is there something wrong I have done in using JSON.stringify?
Javascript Code to call WCF
function Update() {
var myarray1 = new Array();
myarray1[0] = 1;
myarray1[1] = 11;
myarray1[2] = 14;
WCFService1.AJAXEnabledService.BatchUpdateRecords(
JSON.stringify({recordIDs: myarray1}) , "ddsd", "gggg",
updateGrid, OnError);
}
WCF method being called by above JavaScript
[OperationContract]
public bool BatchUpdateRecords(List<int> recordIds, string columnNameToUpdate, string columnValue)
{
DataTable tierIDsTable = new DataTable("RecordIds");
tierIDsTable.Columns.Add(new DataColumn("Integer", typeof(Int32)));
tierIDsTable.PrimaryKey = new DataColumn[] { tierIDsTable.Columns["TierId"] };
foreach (int recordId in recordIds)
{
tierIDsTable.Rows.Add(recordId);
}
return true;
}
Not 100% sure, but have you tried this?
WCFService1.AJAXEnabledService.BatchUpdateRecords(
myarray1,
"ddsd",
"gggg",
updateGrid, OnError);
The issue (without knowing the error that you are receiving) is most likely that you are trying to pass in multiple parameters types. WCF does not usually support and expects an object instead. Create a response class with your parameters and use that instead.
public class ResponseObject
{
public List<int> recordIds { get; set; }
public string columnNameToUpdate { get; set; }
public string columnValue { get; set; }
}
Use this object as your parameter
public bool BatchUpdateRecords(ResponseObject responseObject)
{...

Error 500 when calling asmx service from javascript

I am calling a .net 2.0 asmx service using javascript and servicereference. When I call the service in the test environment and locally, everything works fine. In production I get an error 500. I don't see any errors in the event log or displayed anywhere.
If I hit the web service directly then I receive the json formatted data. If I call the web service from javascript using the servicereference then I get the error 500.
I think this might be related to a size limitation since I can reduce the number of datapoints and the data is returned through javascript correctly.
I have tried increasing the maxRequestLength in httpRuntime but that doesn't have any affect. Is there some setting that I am missing?
If you view this page: http://bit.ly/NXBFpD you will see that data is returned without an error.
If you view this page and watch the calls being made in firebug you'll see that the same page as above is called, but an error 500 is returned. http://bit.ly/NXBPO1
This is the code-behind on the page being called:
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference("/Services/FusionService.asmx"));
}
}
}
This is the js code calling the service:
function GetGeometries(sw, ne, metro) {
FusionService.GetGeometries(sw.lat(), sw.lng(), ne.lat(), ne.lng(), metro, drawMap);
}
This is the service:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class FusionService : System.Web.Services.WebService
{
private string url = "https://www.googleapis.com/fusiontables/v1/query";
[WebMethod]
[ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
public string GetGeometries(decimal southWestLat, decimal southWestLng, decimal northEastLat, decimal northEastLng, string metro)
{
try
{
var jss = new JavaScriptSerializer();
string key = string.Format("Geometry-{0}", metro);
var geometryData = HttpContext.Current.Cache[key] as string;
if (!string.IsNullOrEmpty(geometryData))
{
var deserialized = jss.Deserialize<GeometryData>(geometryData);
return jss.Serialize(deserialized);
}
var query = "SELECT name, geometry, FranchiseI, latitude, longitude, STATE FROM " +
"1JNOz_lhP1B8oCzeNLBm8u5k6ezdRDKtM-NBD8 where ST_INTERSECTS(geometry, RECTANGLE(LATLNG("
+ southWestLat + "," + southWestLng + "), LATLNG(" + northEastLat + "," + northEastLng + ")))";
var requestString = url + "?sql=" + HttpUtility.UrlEncode(query) + "&key=AIzaSyCXLd2VlvZ0FNLbgMKsfSq7Uvp3IDwa";
var request = WebRequest.Create(requestString) as HttpWebRequest;
using (var response = request.GetResponse() as HttpWebResponse)
{
var reader = new StreamReader(response.GetResponseStream());
var json = reader.ReadToEnd();
json = json.Replace("NaN", "\"\"");
HttpContext.Current.Cache[key] = json;
var deserialized = jss.Deserialize<GeometryData>(json);
return jss.Serialize(deserialized);
}
}
catch(Exception ex)
{
return ex.Message;
}
}
}
This was being caused by a limit in the size that can be serialized by JSON. I needed to add this to my web.config.
<system.web.extensions>
<scripting>
<!--The Following lines value should be true in production server-->
<scriptResourceHandler enableCompression="false" enableCaching="true" />
<webServices>
<jsonSerialization maxJsonLength="5000000">
</jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>
Call the URL from the browser on the server and you'll likely see the contents of the error. Alternatively - change your web.config, so you can see the actual error from a client browser as well:
Set:
<customErrors mode="Off" />
This will allow you to track down the issue and resolve it!

Categories