I'm sending JSON data to my Spring API but I always get a bad request. I have tried some things. At first, chanceReward was of type Map<String, Object>. Later I thought it should be a String but it still had a bad request. I researched and thought I needed consumes = "application/json" in the annotation but result is the same. Not sure anymore what to do. Below is the code for my API:
#RequestMapping(value = "/chance/{id}/saveChanceRewards", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
public #ResponseBody Map<String, Object> saveChanceRewards(#PathVariable("id") String id,
#RequestBody String chanceRewards) {
try {
JSONArray jsonArray = new JSONArray(chanceRewards);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject JObject = jsonArray.getJSONObject(i);
System.out.println(JObject.getString("name") + " " + JObject.getString("weight"));
}
} catch(JSONException e) {
_log.error("Error parsing JSON");
}
Map<String, Object> map = new HashMap<String, Object>();
// TODO
return map;
}
Below is the ajax code (inside a .jsp):
let arrayRewards = [];
// get the data from dynamic list of text fields
for (let i = 1; i <= chanceRewardCount; i++) {
arrayRewards.push({
name: $('#chanceRewardName' + i).val(),
weight: $('#chanceRewardWeight' + i).val()
});
}
let data = {'data': arrayRewards};
let jsonData = JSON.stringify(data);
$.ajax({
type: 'post',
dataType: "json",
data: data,
contentType: 'application/json',
url: "${home}/chance/${id}/saveChanceRewards",
method: 'post',
success: function(response) {
console.log('response', response);
},
error: function(err) {
console.log('error', err);
}
});
I'm using Spring Framework 3.2.1.
The 400 Bad Request error is an HTTP status code that means that the request you sent to the website server, often something simple like a request to load a web page, was somehow incorrect or corrupted and the server couldn't understand it.
That mean the server not able to understand the request from your ajax.
First, change #RequestBody String chanceRewards to #RequestBody ChanceRewards chanceRewards
And define ChanceRewards and ChanceReward class.
class ChanceReward {
private String name;
private String weight;
// Getter Setter ...
}
class ChanceRewards {
private List<ChanceReward> data;
// Getter Setter ...
}
If still failed, try open inspect mode and click network tab to check the request send from ajax.
Replace double quotes in your url: "${home}/chance/${id}/saveChanceRewards", by backtick.
There are quite a few things going on here, so let's work on them!
First, I see you've stringified the data into jsonData, but your actual ajax post has data: data instead. Easy fix, just swap in the right variable.
Second thing I notice is that you're wrapping the rewards array in an object (with data = {'data': arrayRewards}) but your Java code expects the array itself (JSONArray) right out of the request body. So this will also throw an exception. You don't have to wrap the array with an object if it's not needed.
Lastly, you mention that you always get a "bad request", but what exactly do you mean? An "HTTP 400" error? Some other HTTP error? It might be useful to give more info on the exact error(s) you see on the javascript side and on the Java server side.
All the other things like worrying about making a ChanceReward / ChanceRewards class, accepts/consumes/produces headers, etc., are superfluous at this point. They are boilerplate niceties and you don't need any of them for this to work correctly.
Related
Hello everyone i have a simple function that will receive double from my server and then it should parse this decimal to double and add it to the total amount variable
for (int i = 0; i < purchase.length; i++){
currentTotalPrice = currentTotalPrice + purchase[i]['total_price'];
}
the thing is i am receiving the total price which is a decimal in the database in a String format and when i try to do something like (purchase[i]['total_price']).toDouble(); the compiler throws
Class 'String' has no instance method 'toDouble'.
Receiver: "15.00"
Tried calling: toDouble()
i don't know why in other parts of my code it worked correctly
this is how i am receiving the data
Future<List> _fetchPurchases() async {
final prefs = await SharedPreferences.getInstance();
String accessToken = prefs.getString("accessToken");
final response = await http.get(
'$webSiteUrl/api/admin/purchases/${article['id']}/$_billNumber',
headers: <String, String>{
'Content-Type': 'application/json;',
'Authorization': 'Bearer $accessToken',
},
);
if(response.statusCode == 200){
List purchases = json.decode(response.body);
return purchases;
}else{
return showErrorMessage(context);
}
}
Note: I am Aware of the Constructor solution where you create a class and then factory the data to custom dart object but for some circumstances i can't do that unless i refactor a very large part of the code so if there is a direct way like toDouble() method it would be the best solution
Indeed toDouble does not exist in the String class, but if you must use it, you can create an extension and import it, something like this
extension NumberParsing on String {
double toDouble() {
return double.parse(this);
}
}
check this gist https://gist.github.com/fcontreras/d5095da7daa0ce24e0f3cb157b91e97f
I have an array that I'm converting to JSON using JSON.stringify
const arrayOfUpdatesAsJSON = JSON.stringify(this.ArrayOfTextUpdates);
This outputs some valid JSON.
[{"key":"AgentName","value":"Joe Blogs"},{"key":"AgentEmail","value":"Joe#test.com"}]
As I'm going to be sending JSON to the server I set the content type to application/json
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
})
};
When a button is pressed I make the request with the url, body and header.
try {
this.httpservice
.post(
url,
arrayOfUpdatesAsJSON,
httpOptions
)
.subscribe(result => {
console.log("Post success: ", result);
});
} catch (error) {
console.log(error);
}
This works fine and hits the method I'm expecting inside the api.
[HttpPost("{id:length(24)}", Name = "UpdateLoan")]
public IActionResult Update(string id, string jsonString)
{
Console.WriteLine(jsonString);
... and some other stuff
}
The ID is populated inside the url builder which populates ok. I would then expect the contents of my variable jsonString inside the api to be populated with the json of my request however it is always null. What am I missing?
Firstly you need to mark jsonString with [FromBody] to tell model binder bind the parameter from posted json. And because you are expecting plain string value you need to pass valid json string (not object) so you need to call additional JSON.stringify in javascript
const jsonArray = JSON.stringify(this.ArrayOfTextUpdates);
const arrayOfUpdatesAsJSON = JSON.stringify(jsonArray);
this.httpservice
.post(
url,
arrayOfUpdatesAsJSON,
httpOptions
)
Controller
[HttpPost("{id:length(24)}", Name = "UpdateLoan")]
public IActionResult Update(string id, [FromBody] string jsonString)
I'm working on integrating the watson-speech.js javascript library with a Spring-based server using the Watson Java SDK. I'm trying to send the output from a WatsonSpeech.SpeechToText.recognizeMicrophone call to the server with no luck. The Speech java classes appear to have the appropriate #SerializedName annotations that match the json being sent from the client, but I'm getting UnrecognizedPropertyException errors from Jackson.
Unrecognized field "keywords_result" (class com.ibm.watson.developer_cloud.speech_to_text.v1.model.SpeechResults), not marked as ignorable (2 known properties: "resultIndex", "results"])
Here's the controller method:
#RequestMapping(value = "/postWatsonRequest", method = RequestMethod.POST)
#ResponseBody
#ResponseStatus(value=HttpStatus.OK)
public ResponseObject postWatsonRequest(#RequestBody SpeechResults speechResults) {
...
}
I'm clearly missing something. Do I need to unpack the json manually on the server side (custom deserializer?) or format it into an acceptable json string on the client side?
It turned out to be a couple of mistakes on my part and although I'm not sure this is the best solution it does work. Here's the full code for anyone that's interested. Key things that made it work:
You must use the receive-jason event to capture the full json result. The data event appears to only return the final text
The result data had to be wrapped in a valid json wrapper - data:{message:data} (this was my big mistake)
Do not include contentType: 'application/json; charset=utf-8', in the ajax call or the controller will not recognize the json data
The Watson Java SDK WebSocketManager receives an okhttp3.ResponseBody from Watson from which it extracts a string. I presume this is similar to what the javascript SDK receives so I used the same code from the WebSocketManager to convert the JSON.stringify string to a SpeechResults object in the controller.
From the okhttp3.ResponseBody javadoc:
A one-shot stream from the origin server to the client application with the raw bytes of the response body
Watson javascript
function listen(token) {
stream = WatsonSpeech.SpeechToText.recognizeMicrophone({
token: token,
readableObjectMode: true,
objectMode: true,
word_confidence: true,
format: false,
keywords: keywordsArray,
keywords_threshold : 0.5,
continuous : false
//interim_results : false
//keepMicrophone: navigator.userAgent.indexOf('Firefox') > 0
});
stream.setEncoding('utf8');
stream.on('error', function(err) {
console.log(err);
stream.stop();
});
stream.on('receive-json', function(msg) {
console.log(msg);
if (msg.state != 'listening') {
if (msg.results[0].final) {
console.log('receive-json: ' + msg);
postResults(msg);
stream.stop();
}
}
});
}
Ajax post
function postResults(results) {
var data = JSON.stringify(results);
console.log('stringify: ' + data);
$.ajax({
type: 'POST',
url: appContextPath + '/postWatsonResult',
dataType: 'json',
data: {message:data}
})
.done(function(data) {
console.log('done data: '+ data);
})
.fail(function(jqXHR, status, error) {
var data = jqXHR.responseJSON;
console.log('fail data: '+ data);
});
}
Spring controller
#RequestMapping(value = "/postWatsonResult", method = RequestMethod.POST)
#ResponseBody
#ResponseStatus(value=HttpStatus.OK)
public ResponseObject postWatsonResult(#RequestParam("message") String message, Locale locale) {
logger.info("postWatsonRequest");
JsonObject json = new JsonParser().parse(message).getAsJsonObject();
SpeechResults results = null;
if (json.has("results")) {
results = GSON.fromJson(message, SpeechResults.class);
}
if (results != null) {
logger.debug("results: " + results.getResults().get(0).getAlternatives().get(0).getTranscript());
}
return new ResponseObject();
}
I still think it should be possible somehow to use #RequestBody SpeechResults speechResults so I'll continue to play around with this, but at least I have a working solution.
I try to send data in JSON format from angularJS client thanks to post http request and get it thanks to j2ee servlet. But I meet a mistake. My complete data can be access thanks to getParameterNames method in my servlet and I can't get it in other way.
I don't understand why my data is the Key and not a value.
AngularJS Client
setParametersForTools : function (toolName, data) {
var jsonData = JSON.stringify(data) // I try with json and stringify json
var promise =
$http({
url: configuration.root_url_api+"SetParametersServlet?tool="+toolName,
method: "POST",
dataType: 'json',
data: data,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
.then(function (response){
console.log(response);
}, function (error){
console.log(error);
})
return promise;
}
Servlet
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String toolname = request.getParameter("tool"); //toolname is correct
String json = request.getParameter("data"); // return null...
Enumeration<String> paramsName = request.getParameterNames();
for (;paramsName.hasMoreElements();) {
String paramName=paramsName.nextElement();
System.out.println("param:"+paramName);
}
}
Servlet log
//For Parameter names
param:tool
param:{ my correct data in json format}
Maybe I don't send data correctly but after many searches I don't understand what's wrong.
Please make the following changes in your code.
setParametersForTools : function (toolName, data) {
$http.post(configuration.root_url_api+"SetParametersServlet?tool="+toolName, data)
.then(function (response){
console.log(response);
}, function (error){
console.log(error);
});
}
If you want to use json I'd suggest to implement JAX-RS service instead of Servlet, it will be much easier, especially if you will later use some more complex json.
Pseudo code for service (you will need to add jax-rs configuration to web.xml also):
#Path("/myPath")
public class MyService {
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public OutputData setParameters(InputData data, #QueryParam("tool") String tool) {
System.out.println("Input data: " + data);
System.out.println("Tool name: " + tool);
...
return outputData;
}
}
where inputData and outputData are Java objects representing json.
I found a good answer with this post.
$httpParamSerializer method fixes the problem
I am a beginner in liferay portlet development and I am developing a portlet that receives a http get request, processes some information and than it has to return a json object. My problem is that my portlet sends a whole html page instead of just the json object.
This is my code:
HttpServletResponse servletResponse = PortalUtil.getHttpServletResponse((renderResponse));
servletResponse.setHeader("Content-type", "application/json");
servletResponse.setCharacterEncoding("application/json");
PrintWriter out = servletResponse.getWriter();
out.write(EntityUtils.toString(responseEntity));
out.flush();
out.close();
I execute this in the doView() method I know that this is not the best practice, but I am not concerned with that at the moment. Can someone explain to me how to return just the json object I read something about serveResponse, but I couldn't figure out how to invoke it.
Well, one thing to notice, that the doView() is mostly responsible for rendering of your portlet. Your requirement can better achieved by
1 - processAction(Portlet Action) or
2 - serveResource(Portlet AJAX service).
To my view, AJAX request-response will be the most suitable case; for that you just need to create a resource URL on your portlet view. Like:
<portlet:resourceURL var="ajaxResourceURL" />
Add a JavaScript method to the page, from where you can generate AJAX request to your portlet. The method will look something like this,
<script type="text/javascript">
function _callAjax(val1, val2){
var url = '<%=ajaxResourceURL %>'; // pass resource URL you created using scriplet / EL.
jQuery.ajax({
type : "POST",
url : url,
cache:false,
dataType: "json",
data: {
value1: val1, // extra parameters if you want to pass
value2: val2
},
success : function(data){
// do whatever you want with your response data
},
error : function(XMLHttpRequest, textStatus, errorThrown){
alert(errorThrown);
alert(textStatus);
}
});
};
</script>
Call that ajax method on a button / link click event:
<input type="button" onclick="_callAjax('val1', 'val2')" value="Send" />
And finally, in your portlet's action listener class add the following serveResource method, responsible for handling AJAX based request(s).
Here you can get your request parameters and generate a response in the sense you want:
#Override
public void serveResource(ResourceRequest request, ResourceResponse response) throws PortletException,IOException {
String value1 = ParamUtil.getString(request , "value1", "default"); // request parameters
String value2 = ParamUtil.getString(request , "value2", "");
PrintWriter writer = response.getWriter();
JSONObject jsonObject = new JSONObject();
jsonObject.put(String key, boolean/long/Collection/double/Map/Object value);
writer.write(jsonObject.toString());
}
Thats it! Hope, this will be helpful for you :)