Spring returns gibberish to JS client - json

Here is the response I get from my WebSocket controller whenever it publishes a message to it's subscribers
n {command: "MESSAGE", headers: {…}, body: "{\"headers\":{},\"body\":{\"sender\":\"b\",\"recipient\":\"my…dy\":\"a\"},\"statusCode\":\"OK\",\"statusCodeValue\":200}", escapeHeaderValues: true, ack: ƒ, …}
ack: ƒ (e)
body: "{\"headers\":{},\"body\":{\"sender\":\"b\",\"recipient\":\"myself\",\"body\":\"a\"},\"statusCode\":\"OK\",\"statusCodeValue\":200}"
command: "MESSAGE"
escapeHeaderValues: true
headers: {content-length: "108", message-id: "ec3f952a-e9f6-2d4e-997b-07bd18bd5d58-14", subscription: "sub-0", content-type: "application/json", destination: "/topic/greetings"}
Everything gets garbled into one ugly string under "body", even though when I log the messages in my server, I see a proper JSON object.
Here is my controller
public class WebsocketController {
private static final Logger LOGGER = LoggerFactory.getLogger(WebsocketController.class) ;
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public ResponseEntity<MyMessage> webSocketMessage(MyMessage message) {
LOGGER.info("Msg is : " + message);
return new ResponseEntity<>(message, HttpStatus.OK);
}
}
And my JS client
function connect() {
var socket = new WebSocket('ws://localhost:8081/gs-guide-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', (messageOutput) => {
console.log(messageOutput);
showMessageOutput(JSON.parse(messageOutput.body));
});
});
}

Solved the problem, but still don't understand some parts:
My response is already a JSON. However, the body is a string NOT in JSON form, so I wrote this to fix it:
console.log(JSON.parse(messageOutput.body).body);
It's a solution, but it smells. If someone could shed some light on why the body of the response came as a string and not as a JSON it would be great.

Related

Angular 2 and .NET Core Web Api: http post does not call the API

I'm trying to perform a post request to my WebAPI controller, but there is no way to make my action to be called.
This is the action:
[HttpPost("about")]
public async Task<IActionResult> PostAbout([FromBody] PostAboutBindingModel model)
{
if (model == null || !ModelState.IsValid)
return BadRequest(ModelState);
var about = new About
{
Text = model.Text,
Date = model.Date,
Images = _jsonSerializer.Serialize(model.Images)
};
_context.Abouts.Add(about);
await _context.SaveChangesAsync();
return Created($"/api/about/{about.Version}", about);
}
The PostAboutBindingModel has only three properties: Text, Date and Images.
This is the angular2 code snippet where I perform the API call:
let model: IAbout = <IAbout>{
date: new Date(),
images: [],
text: "test"
}
let opts = jwtAuthorization();
opts.headers.append("Content-Type", "application/json");
return this.http.post("/api/about", model, opts)
.map((response: Response) => console.log("TEST", response.json()))
.catch(this.handleError);
The jwtAuthorization simply add the Authorization header:
export function jwtAuthorization(): RequestOptions {
"use strict"
if (localStorage.getItem("auth")) {
// create authorization header with jwt token
let auth: IAuth = getAuth(JSON.parse(atob(localStorage.getItem("auth"))));
if (auth && auth.access_token) {
let headers: Headers = new Headers({ "Authorization": auth.token_type + " " + auth.access_token });
return new RequestOptions({ headers: headers });
}
}
}
I've tried to specify, as body, the following things:
model
{ model }
{ model: model }
JSON.stringify(model)
JSON.stringify({ model: model })
I've tried to specify my model as a generic object (without type) too.
I've tried to perform the call with and without the Content-Type header.
None of the previous seems to work. The API action is not called and no errors are returned.
I would like to perform the request specify only model as-is if it's possible but I would be happy in any case, if it works :)
What am I missing?
EDIT
I read now that http requests in angular 2 are "lazy" so they need a subscriber (subscribe) to work.
Thanks for help

Watson SpeechToText Java and javascript model differences

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.

How to return a JSON object from an Azure Function with Node.js

With Azure Functions, what do you need to do to return a JSON object in the body from a function written in node.js? I can easily return a string, but when I try to return a json object as shown below I appear to have nothing returned.
context.res = {
body: jsonData,
contentType: 'application/json'
};
Based on my recent testing (March 2017). You have to explicitly add content type to response headers to get json back otherwise data shows-up as XML in browser.
"Content-Type":"application/json"
res = {
status: 200, /* Defaults to 200 */
body: {message: "Hello " + (req.query.name || req.body.name)},
headers: {
'Content-Type': 'application/json'
}
};
Full Sample below:
module.exports = function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
context.log(context);
if (req.query.name || (req.body && req.body.name)) {
res = {
// status: 200, /* Defaults to 200 */
body: {message: "Hello " + (req.query.name || req.body.name)},
headers: {
'Content-Type': 'application/json'
}
};
}
else {
res = {
status: 400,
body: "Please pass a name on the query string or in the request body"
};
}
context.done(null, res);
};
If your data is a JS object, then this should just work, e.g.
module.exports = function(context, req) {
context.res = {
body: { name: "Azure Functions" }
};
context.done();
};
This will return an application/json response.
If instead you have your data in a json string, you can have:
module.exports = function(context, req) {
context.res = {
body: '{ "name": "Azure Functions" }'
};
context.done();
};
Which will return an application/json response because it sniffs that it is valid json.
module.exports = function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
if (req.query.name || (req.body && req.body.name)) {
context.res = {
// status: 200, /* Defaults to 200 */
body: {"data":"Hello"},
headers: {
'Content-Type': 'application/json'
}
};
}
else {
// res = {
// status: 400,
// body: "Please pass a name on the query string or in the request body"
// };
}
context.done(null,res);
I would like to add one more point. Apart from making the body: a JSON object, the request should also contain proper headers telling server what content type we are interested in. I could see that same Azure function when just invoked via browser using URL gives XML response, but when invoking from script or tools like Postman it gives JSON.
I feel like the answer has been given but it hasn't been clearly presented so I thought I'd answer as well in case it will help anyone coming behind me. I too have created a function that most definitely returns a Javascript object but if I copy and paste the URL in the Azure Function UI and just open a new tab in Chrome and try to view the output, I actually get back an XML document that tells me there's an error (not surprising there's an error as many characters in the Javascript would have blown up the XML). So, as others have mentioned, the key is sending the appropriate headers with your request. When you copy/paste the URL into your browser, the browser is sending a request header that looks similar to this:
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8
When that happens, you see the XML return as described in this link:
https://github.com/strongloop/strong-remoting/issues/118
In order to get around this problem and see what the data would look like with a JSON request, either use a utility like Postman:
https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en
Accept: application/json
Or use a CURL command and pass in the proper Accept header.
As you can see in the screenshot above, when I provided the proper header, I get back the JSON response I would expect.
You can also use JSON.stringify() to make a valid json string out of your js-object:
jsonData = { value: "test" }:
context.res = {
body: JSON.stringify(jsonData)
};

Spring MVC to send responsebody object to ajax post gives 406 error

There are similar links but I haven't found any solution to work for me, so I was wondering if someone could give me a working example for my scenario. I am doing an ajax get to retrieve data from the server side, so I can create charts dynamically on the client side. Do I need to include MappingJacksonHttpMessageConverter? If that's the answer can someone provide an example i can follow for that?
Java:
#RequestMapping(value="/getReportData.html", method=RequestMethod.GET, produces="application/json")
public #ResponseBody Reports getReport1Data(HttpServletRequest request)
{
System.out.println("Report 1 Page GET Method");
ModelAndView mv = new ModelAndView("report1");
if((Reports)request.getSession().getAttribute(USER_SESSION_REPORTS) != null){
reports = (Reports)request.getSession().getAttribute(USER_SESSION_REPORTS);
System.out.println("--------> Report 1 Page with session data");
return reports;
}
else{
System.out.println("--------> Report 1 Page with NO session data");
}
mv.addObject("report1", reports.getReport1());
return null;
}
Javascript:
function getData(){
$.ajax({
url: "getReportData.html",
type: "GET",
contentType: "application/json",
dataType: JSON,
success: function(report1){
console.log("success: " + report1.utilRatio.decRatio);
},
error: function(report1){
console.log("error: " + report1.utilRatio.decRatio);
}
});
}
Response Headers:
Content-Language: "en",
Content-Length: "1110"
Content-Type: "text/html;charset=utf-8"
Server: "Apache-Coyote/1.1"
Request Headers:
Accept: "/"
Accept-Language: "en-US,en;q=0.5"
Accept-Encoding: "gzip,deflate"
Content-Type: "application/json"
X-Requested-With: "XMLHttpRequest"
It looks like your request headers are wrong. You can remove the contentType setting since you are not sending data to the server and change dataType to the string value "json" instead of the variable JSON.
Also, your response headers are wrong. Just make sure you are always returning a Reports object. And you probably want to remove the html extension from that endpoint since you're just returning an object.
spring uses #ResponseBody annotaion for returning data as json .it will implicitly call the MappingJacksonHttpMessageConverter .so you need to use it.
#RequestMapping(value = "/getjson", method = RequestMethod.POST, produces = "application/json")
#Transactional
public void getJson(HttpServletRequest request, HttpServletResponse response, #RequestParam("type") String type)
throws DatatypeConfigurationException, IOException, JSONException {
JSONObject json = new JSONObject();
Map<String, String[]> parameterMap = request.getParameterMap();
List<Chart> chart=myService.getChart();
if (Chart.size()>0) {
json.put("status", "SUCCESS");
JSONArray array = new JSONArray();
for (Chart chartData: chart) {
JSONObject object = new JSONObject();
object.put("id", chartData.getRaasiId());
object.put("name", chartData.getName());
array.put(object);
}
json.put("options", array);
}
}
}
response.setContentType("application/json");
System.out.println("response======" + json.toString());
PrintWriter out = response.getWriter();
out.write(json.toString());
}
============
on the html
jQuery
.ajax({
url : controllerUrl,
dataType : 'text',
processData : false,
contentType : false,
type : 'GET',
success : function(response) {
success : function(response) {
marker = JSON.stringify(response);
json = jQuery.parseJSON(marker);
json = JSON.parse(json);
alert(json.status);
}
});
for reference:
https://rwehner.wordpress.com/2010/06/09/2-ways-to-create-json-response-for-ajax-request-in-spring3/

Spring MVC Multipart Request with JSON

I want to post a file with some JSON data using Spring MVC. So I've developed a rest service as
#RequestMapping(value = "/servicegenerator/wsdl", method = RequestMethod.POST,consumes = { "multipart/mixed", "multipart/form-data" })
#ResponseBody
public String generateWSDLService(#RequestPart("meta-data") WSDLInfo wsdlInfo,#RequestPart("file") MultipartFile file) throws WSDLException, IOException,
JAXBException, ParserConfigurationException, SAXException, TransformerException {
return handleWSDL(wsdlInfo,file);
}
When I send a request from the rest client with
content-Type = multipart/form-data or multipart/mixed, I get the next exception:
org.springframework.web.multipart.support.MissingServletRequestPartException
Can anyone help me in solving this issue?
Can I use #RequestPart to send both Multipart and JSON to a server?
This is how I implemented Spring MVC Multipart Request with JSON Data.
Multipart Request with JSON Data (also called Mixed Multipart):
Based on RESTful service in Spring 4.0.2 Release, HTTP request with the first part as XML or JSON formatted data and the second part as a file can be achieved with #RequestPart. Below is the sample implementation.
Java Snippet:
Rest service in Controller will have mixed #RequestPart and MultipartFile to serve such Multipart + JSON request.
#RequestMapping(value = "/executesampleservice", method = RequestMethod.POST,
consumes = {"multipart/form-data"})
#ResponseBody
public boolean executeSampleService(
#RequestPart("properties") #Valid ConnectionProperties properties,
#RequestPart("file") #Valid #NotNull #NotBlank MultipartFile file) {
return projectService.executeSampleService(properties, file);
}
Front End (JavaScript) Snippet:
Create a FormData object.
Append the file to the FormData object using one of the below steps.
If the file has been uploaded using an input element of type "file", then append it to the FormData object.
formData.append("file", document.forms[formName].file.files[0]);
Directly append the file to the FormData object.
formData.append("file", myFile, "myfile.txt"); OR formData.append("file", myBob, "myfile.txt");
Create a blob with the stringified JSON data and append it to the FormData object. This causes the Content-type of the second part in the multipart request to be "application/json" instead of the file type.
Send the request to the server.
Request Details:
Content-Type: undefined. This causes the browser to set the Content-Type to multipart/form-data and fill the boundary correctly. Manually setting Content-Type to multipart/form-data will fail to fill in the boundary parameter of the request.
Javascript Code:
formData = new FormData();
formData.append("file", document.forms[formName].file.files[0]);
formData.append('properties', new Blob([JSON.stringify({
"name": "root",
"password": "root"
})], {
type: "application/json"
}));
Request Details:
method: "POST",
headers: {
"Content-Type": undefined
},
data: formData
Request Payload:
Accept:application/json, text/plain, */*
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryEBoJzS3HQ4PgE1QB
------WebKitFormBoundaryvijcWI2ZrZQ8xEBN
Content-Disposition: form-data; name="file"; filename="myfile.txt"
Content-Type: application/txt
------WebKitFormBoundaryvijcWI2ZrZQ8xEBN
Content-Disposition: form-data; name="properties"; filename="blob"
Content-Type: application/json
------WebKitFormBoundaryvijcWI2ZrZQ8xEBN--
This must work!
client (angular):
$scope.saveForm = function () {
var formData = new FormData();
var file = $scope.myFile;
var json = $scope.myJson;
formData.append("file", file);
formData.append("ad",JSON.stringify(json));//important: convert to JSON!
var req = {
url: '/upload',
method: 'POST',
headers: {'Content-Type': undefined},
data: formData,
transformRequest: function (data, headersGetterFunction) {
return data;
}
};
Backend-Spring Boot:
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public #ResponseBody
Advertisement storeAd(#RequestPart("ad") String adString, #RequestPart("file") MultipartFile file) throws IOException {
Advertisement jsonAd = new ObjectMapper().readValue(adString, Advertisement.class);
//do whatever you want with your file and jsonAd
You can also use the next way a list List<MultipartFile> and #RequestPart("myObj") as parameters in your method inside a #RestController
#PostMapping()
#ResponseStatus(HttpStatus.CREATED)
public String create(#RequestPart("file") List<MultipartFile> files, #RequestPart("myObj") MyJsonDTOClass myObj) throws GeneralSecurityException, IOException {
// your code
}
and in the axios side with a bit of react:
const jsonStr = JSON.stringify(myJsonObj);
const blob = new Blob([jsonStr], {
type: 'application/json'
});
let formData = new FormData();
formData.append("myObj",blob );
formData.append("file", this.state.fileForm); // check your control
let url = `your url`
let method = `post`
let headers =
{
'Accept': 'application/json',
'Content-Type': 'application/json'
};
}
axios({
method,
url,
data: formData,
headers
}).then(res => {
console.log(res);
console.log(res.data);
});
We've seen in our projects that a post request with JSON and files is creating a lot of confusion between the frontend and backend developers, leading to unnecessary wastage of time.
Here's a better approach: convert file bytes array to Base64 string and send it in the JSON.
public Class UserDTO {
private String firstName;
private String lastName;
private FileDTO profilePic;
}
public class FileDTO {
private String base64;
// just base64 string is enough. If you want, send additional details
private String name;
private String type;
private String lastModified;
}
#PostMapping("/user")
public String saveUser(#RequestBody UserDTO user) {
byte[] fileBytes = Base64Utils.decodeFromString(user.getProfilePic().getBase64());
....
}
JS code to convert file to base64 string:
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
const userDTO = {
firstName: "John",
lastName: "Wick",
profilePic: {
base64: reader.result,
name: file.name,
lastModified: file.lastModified,
type: file.type
}
}
// post userDTO
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
As documentation says:
Raised when the part of a "multipart/form-data" request identified by
its name cannot be found.
This may be because the request is not a multipart/form-data either
because the part is not present in the request, or because the web
application is not configured correctly for processing multipart
requests -- e.g. no MultipartResolver.
For Angular2+ users. Try to send JSON payload in a mixed part request as below.
formData.append("jsonPayload", new Blob([JSON.stringify(json)], {
type: "application/json"
}));
Given below complete function.
submit() {
const formData = new FormData();
formData.append('file', this.myForm.get('fileSource').value);
var json = {
"key":"value"
};
formData.append("jsonPayload", new Blob([JSON.stringify(json)], {
type: "application/json"
}));
this.http.post('http://localhost:8080/api/mixed-part-endpoint', formData)
.subscribe(res => {
console.log(res);
alert('Uploaded Successfully.');
})
}