Spring MVC, parsing JSON without writing additional classes - ajax

I am new to Spring MVC and trying to re-write an app already written in PHP.
I want to pass a complex JSON string to the spring MVC controller (using AJAX post) so that it can save it in the database. I just need to extract few values from the JSON in the controller and hence would not like to create classes for the entire structure.
I just need the templateName and templateId from this JSON object. Is there a way I can extract some values from the JSON object.
I have written the below JSON to give an example:
Sample JSON Structure:
{
"templateName": "template1",
"templateId": "t01",
"templateObj": {
"name" : "mytemplate",
"values": [1,2,3,4,5,6],
"drawings": [
"circle": {
"radius": 20,
"color" : "#f3f3f3",
"border": 1
},
"rectangle":{
"width": 20,
"height": 40,
"color": "#f1f1f1"
}
]
}
}
Javascript Code:
$.ajax({
type: 'POST',
url: url,
data: JSON.stringify(templateObj),
...
Java code -
#RestController
public class SaveChartConfigController {
#RequestMapping(value= "/saveChartConfig")
public #ResponseBody String processSaveChartConfig(#ModelAttribute JSONObject jsonObj) {

Actually, you can by doing this:
#RequestMapping(value= "/saveChartConfig")
public #ResponseBody String processSaveChartConfig(#RequestBody String json) throws JsonParseException, JsonMappingException, IOException {
Map<String, Object> jsonToMap = new ObjectMapper().readValue(json, Map.class);
String templateName = (String) jsonToMap.get("templateName"); //here you get the parameters
return templateName; //For example
}
Jackson Jars:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
Note: import org.codehaus.jackson.map.ObjectMapper;
But, in your case, it will not work because your JSON it's not a valid JSON Object. If you fix this, then the code above it will solve your problem.

Related

spring boot with restcontroller using jsonobject not working

I am using spring boot, and while making a restcontroller or controller if I use the jsonobject type request then it doesnt work, whereas same works when I change type to string.
#Controller
#RequestMapping("rest/dummy")
public class CustomerController {
#GetMapping("test")
public ResponseEntity test(#RequestParam("req") JSONObject inputData) {
org.json.JSONObject response = new org.json.JSONObject();
response.put("abc", "123");
return new ResponseEntity(inputData.toString(), HttpStatus.OK);
}
pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20171018</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
I do want to use it both GET and POST type and also I want to use jsonobject for both request and response as the data can change on fly and its type.
In RequestParam , we send key values which added in URL,
To send Json object send it in RequestBody .
Use #RequestBody and send your Json in body part of your request.
Using real POJOs as params and return values is the better approach imo. Use Jackson annotations to configure those POJOs.
Anyways. This should work:
#GetMapping("test")
public ResponseEntity<String> test(#RequestParam("req") JSONObject inputData) {
org.json.JSONObject response = new org.json.JSONObject();
response.put("abc", "123");
return ResponseEntity.ok(inputData.toString());
}
alternatively
#GetMapping("test")
public ResponseEntity<SomeOutputDto> test(#RequestParam("req") String inputData) {
SomeOutputDto out = new SomeOutputDto();
out.setAbc(123);
return ResponseEntity.ok(dto);
}
this requires a additional class: SomeOutputDto, but on the other hand, you have more control over your code.
public class SomeOutputDto {
private int abc = 0;
public void setAbc(int v) {
this.abc = v;
}
public int getAbc() { return this.abc; }
}
Got it working by using apache-tomcat 8.0.15, the same doesnt work with apache-tomcat 8.0.49

Angular2 does not pass parameters in POST request

I am trying to pass a parameter using Angular POST request to Tomcat server, Spring Framework. Somehow I see that the parameter is there when it is sent, but it somehow does not arrive/properly retrieved on the backend. Here is the Angular2 code:
addCompany() {
console.log("addCompany button clicked!");
console.log("company name: " + this.input);
let nameId = this.input;
let body = JSON.stringify({ input: nameId });
let headers = new Headers({ 'Content-Type': 'application/json', 'X-CSRF-TOKEN':this.getToken() });
console.log("csrf token: " + this.getToken());
let options = new RequestOptions({ headers: headers });
this.http.post('http://localhost:8080/views/addcompany', body, options).toPromise()
.then(() => {
console.log("company added!");
this.reloadList();
});;
}
When I am trying to get it in Spring I am getting null for the parameter:
#RequestMapping(value = "/addcompany", method = RequestMethod.POST)
#ResponseBody
public void addCompany(HttpServletRequest request,
HttpServletResponse response) {
String nameId = request.getParameter("input");
eventService.addCompany(nameId);
}
I tried also this way:
#RequestMapping(value = "/addcompany", method = RequestMethod.POST)
#ResponseBody
public void addCompany(Model model, #RequestParam("input") String nameId) {
eventService.addCompany(nameId);
}
And in Angular code I have been trying to change commas everywhere, like:
let nameId = this.input;
let body = JSON.stringify({ 'input': nameId });
etc.
I tried this one: Angular2 - Http POST request parameters
Following the suggestion JB Nizet I tried to create POJO:
public class Company {
public String input;
public Company() {
this.input = "";
}
public String getInput() {
return this.input;
}
public void setInput(String input) {
this.input = input;
}
}
Register it in my #Configuration file:
#Bean
public Company getCompany(){
return new Company();
}
And changed the request method to the following:
#RequestMapping(value = "/addcompany", method = RequestMethod.POST)
#ResponseBody
public void addCompany(Company company) {
eventService.addCompany(company.input);
}
After that I am getting Company object in the method with input=null.
Then I tried to deregister the Company #Bean from #Configuration and change the request method to the following:
#RequestMapping(value = "/addcompany", method = RequestMethod.POST)
#ResponseBody
public void addCompany(#RequestBody Company company) {
eventService.addCompany(company.input);
}
But after that I am getting 415 (Unsupported Media Type) error.
In pom.xml I have the following jackson import:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.10</version>
</dependency>
Substituting it for second jackson version solved the issue:
<properties>
...
<jackson.version>2.7.5</jackson.version>
</properties>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
You're sending a JSON object as body, and you expect to get a request parameter containing an attribute of this JSON object.
That won't happen. You need a POJO that matches with the structure of the JSON sent, take that POJO as argument and annotate it with #RequestBody. Jackson will unmarshal the JSON to the POJO and the POJO will be passed to your method.
Request parameters can be used if the request contains an application/x-www-form-urlencoded payload: the kind of payload you send when submitting a HTML form, without doing any JavaScript.
Instead of
let body = JSON.stringify({ input: nameId });
try
let body = { input: nameId };
Try to use :
let body:string='input='+nameId;
And use this header
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded', 'X-CSRF-TOKEN':this.getToken() });
Only for other readers if you want to send more than 1 parameter. use something & fro separating parameter . like below code
let body :string='username='+username+'&password='+password;

POST with angular $http and Chrome's REST client gives 400 Bad request error

I am trying to make a REST call to my spring controller on jboss
#RequestMapping(value = "/myMethod", method = RequestMethod.POST)
public
#ResponseBody
String myMethod(#RequestBody MyClass myClass) {
String json = null;
String METHOD_NAME = "getAuditForRecordId";
Gson gson = new GsonBuilder().create();
try {
json = gson.toJson("success");
}
return json;
}
My angular post call
$http.post(<URL to myMethod>, postData,
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}).success(function () {
d.resolve(response);
}).error(function () {
d.reject();
}
);
is failing with a 400 Bad request error. I have a CORSFilter for my jboss app
#Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse response=(HttpServletResponse) resp;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Accept, Content-Type");
chain.doFilter(req, resp);
}
GET methods inside the same controller are working fine. But this above POST fails.
My POST JSON object has nested objects.
I also tried the same POST call from Chrome's REST client but still get the same error. Can someone point to the obvious mistake I am making here?
The error is that the url on the post is allocated in other domain, not in the same domain that are your project running, you dont call url form other domain.
This is my example:
#RequestMapping(value = "/listAll.json", params = {"page","pageSize","order","filter"}, method = RequestMethod.GET, produces={"application/json; charset=ISO-8859-1"})
#ResponseBody
public String listAll(#RequestParam("pageSize") int pageSize, #RequestParam("page") int page, #RequestParam("order") String order, #RequestParam("filter") String filters){
return this.pilmotiformService.listAllIntermediario(pageSize, page, order, filters);
}
#RequestMapping(value = "updateRecord.json", method = RequestMethod.POST, produces={"application/json; charset=ISO-8859-1"})
#ResponseBody
public String UpdateRecord(#RequestParam("cons") Long cons) throws Throwable{}
From Angular:
myApp.service('myService', function($http, $rootScope, $routeParams) {
this.oneFunction = function() {
return $http({
method: 'GET',
url: WEB_SERVER+'/oneMethod.json',
params: {modulo: 'FMT_FORMREGI,FMT_AUDITORIA,FMT_ESTADO,FMT_ADJUNTO,PIL_USUA,PIL_MOTIVO,PIL_MOTIFORM' }
});
}
this.otherFunction = function(one, two, three) {
data = {one : one, two : two, three : three};
return $http({
method: 'POST',
url: WEB_SERVER+'/otherMethod.json',
data: data
});
}
});
Jackson mapper is the component that convert or serialize the data to entitys.
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-lgpl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-lgpl</artifactId>
<version>1.9.13</version>
</dependency>
i am not sure but i think what you are missing here is the that json is in this format
{"key" : "value"}
and you are trying to do "success" so may be this is what you are missing.
I may be wrong as i have not working on gson or json a lot
I found the problem. It's in the data I am passing with my post request. I had one extra property in my passed-in nested Object. When I changed the data model on my server side to include that extra property my issue was resolved.

need to return json data from spring mvc controller

I am trying to return a Map from spring mvc controller making an ajax call but i am not getting correct resposne.
I have used mvc annotation tag in my config file and also included the jackson jar file in my library.
The requirement for me is to return Map to success of my Ajax call so i can modify a table row in html.
Code for the controller :
#RequestMapping(value="/pricingrecall.do", method=RequestMethod.POST)
#ResponseBody
public Map<Integer,String> pricingUpdate(#RequestParam(value = "opp_Code", required = false) String opp_Code,
#RequestParam(value = "ref_id", required = false) String ref_id,
ModelMap model,
HttpServletRequest request, HttpServletResponse response) throws SQLException, Exception{
String User="fe0777";
List<CrossListViewBean>updatedRow = new ArrayList<CrossListViewBean>();
//String message="";
logger.info(methodLocation+"|"+"Calling pricing recall ....");
Map<String, Object> result = new HashMap<String, Object>();
updatedRow=crossCampService.getupdatedrowListview(opp_Code, ref_id, user);
Map<Integer,String> lbean= new HashMap<Integer,String>();
lbean=crossCampService.getUpdatedDataPosition(updatedRow.get(0));
return lbean;
}
Call from Ajax:
jQuery.ajax( {
url : '/Web/pricingrecall.do',
type: "POST",
cache : false,
timeout : 60000,
data : {
opp_Code :CampId ,
ref_id : index
},
success : function(result, textStatus, request) {
if(result)
{
alert(result);
//jQuery(".note"+index).html(data);
}else
{
alert("The user session has timed out. Please log back in to the service.");
window.location.replace("logout.do");
}
},
error : function(request, textStatus, errorThrown) {
alert("The system has encountered an unexpected error or is currently unavailable. Please contact the support number above if you have any questions.");
}
});
Here in the ajax sucess i am always getting error ,it gets diverted tot he error string.
How can i get the Json from MAp in the ajax sucess
Please help
I'm using flexjson to get the json output correct. I'm attaching a sample code of mine, using flexjson. You can use this as a reference and restructure your controller method to output the correcct json. This link will help you in how to serialize a map.
#RequestMapping(value = "/{id}", headers = "Accept=application/json")
#ResponseBody
public ResponseEntity<String> findUser(#PathVariable("id") Long id) {
User user = userService.find(id);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json; charset=utf-8");
return new ResponseEntity<String>(user.toJson(), headers, HttpStatus.OK);
}
#Entity
public class AppUser {
#NotNull
private String firstName;
#NotNull
private String lastName;
//Getter Setter goes here
public String AppUser.toJson() {
return new JSONSerializer().exclude("*.class").serialize(this);
}
}
I used jackson-databind and with #ResponseBody annotation on controller methods, it automatically converted returned data to json successfully. If you use maven, add these dependencies to your pom.xml (jackson.version is 2.4.0 for me).
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
Otherwise, you can add the jar files to your classpath.

POST JSON fails with 415 Unsupported media type, Spring 3 mvc

I am trying to send a POST request to a servlet. Request is sent via jQuery in this way:
var productCategory = new Object();
productCategory.idProductCategory = 1;
productCategory.description = "Descrizione2";
newCategory(productCategory);
where newCategory is
function newCategory(productCategory)
{
$.postJSON("ajax/newproductcategory", productCategory, function(
idProductCategory)
{
console.debug("Inserted: " + idProductCategory);
});
}
and postJSON is
$.postJSON = function(url, data, callback) {
return jQuery.ajax({
'type': 'POST',
'url': url,
'contentType': 'application/json',
'data': JSON.stringify(data),
'dataType': 'json',
'success': callback
});
};
With firebug I see that JSON is sent correctly:
{"idProductCategory":1,"description":"Descrizione2"}
But I get 415 Unsupported media type. Spring mvc controller has signature
#RequestMapping(value = "/ajax/newproductcategory", method = RequestMethod.POST)
public #ResponseBody
Integer newProductCategory(HttpServletRequest request,
#RequestBody ProductCategory productCategory)
Some days ago it worked, now it is not. I'll show more code if needed.
I've had this happen before with Spring #ResponseBody and it was because there was no accept header sent with the request. Accept header can be a pain to set with jQuery, but this worked for me source
$.postJSON = function(url, data, callback) {
return jQuery.ajax({
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
'type': 'POST',
'url': url,
'data': JSON.stringify(data),
'dataType': 'json',
'success': callback
});
};
The Content-Type header is used by #RequestBody to determine what format the data being sent from the client in the request is. The accept header is used by #ResponseBody to determine what format to sent the data back to the client in the response. That's why you need both headers.
adding content type into the request as application/json resolved the issue
I had a similar problem but found the issue was that I had neglected to provide a default constructor for the DTO that was annotated with #RequestBody.
I faced a similar issue and this is how I fixed it,
The problem is due to the conversion process from JSON to Java, one need to have the right run time jackson libraries for the conversion to happen correctly.
Add the following jars (through dependency or by downloading and adding to the classpath.
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.3</version>
</dependency>
This should fix the problem.
Complete Code:
function() {
$.ajax({
type: "POST",
url: "saveUserDetails.do",
data: JSON.stringify({
name: "Gerry",
ity: "Sydney"
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
success: function(data) {
if (data.status == 'OK')
alert('Person has been added');
else
alert('Failed adding person: ' + data.status + ', ' + data.errorMessage);
}
and the controller signature looks like this:
#RequestMapping(value = "/saveUserDetails.do", method = RequestMethod.POST)
public #ResponseBody Person addPerson( #RequestBody final Person person) {
Hope this helps
I believe I ran exactly into the same issue. After countless hours of fighting with the JSON, the JavaScript and the Server, I found the culprit: In my case I had a Date object in the DTO, this Date object was converted to a String so we could show it in the view with the format: HH:mm.
When JSON information was being sent back, this Date String object had to be converted back into a full Date Object, therefore we also need a method to set it in the DTO. The big BUT is you cannot have 2 methods with the same name (Overload) in the DTO even if they have different type of parameter (String vs Date) because this will give you also the 415 Unsupported Media type error.
This was my controller method
#RequestMapping(value = "/alarmdownload/update", produces = "application/json", method = RequestMethod.POST)
public #ResponseBody
StatusResponse update(#RequestBody AlarmDownloadDTO[] rowList) {
System.out.println("hola");
return new StatusResponse();
}
This was my DTO example (id get/set and preAlarm get Methods are not included for code shortness):
#JsonIgnoreProperties(ignoreUnknown = true)
public class AlarmDownloadDTO implements Serializable {
private static final SimpleDateFormat formatHHmm = new SimpleDateFormat("HH:mm");
private String id;
private Date preAlarm;
public void setPreAlarm(Date date) {
this.preAlarm == date;
}
public void setPreAlarm(String date) {
try {
this.preAlarm = formatHHmm.parse(date);
} catch (ParseException e) {
this.preAlarm = null;
} catch (NullPointerException e){
this.preAlarm = null;
}
}
}
To make everything work you need to remove the method with Date type parameter. This error is very frustrating. Hope this can save someone hours of debugging.
I faced this issue when I integrated spring boot with spring mvc. I solved it by just adding these dependencies.
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.3</version>
</dependency>
A small side note - stumbled upon this same error while developing a web application. The mistake we found, by toying with the service with Firefox Poster, was that both fields and values in the Json should be surrounded by double quotes. For instance..
[ {"idProductCategory" : "1" , "description":"Descrizione1"},
{"idProductCategory" : "2" , "description":"Descrizione2"} ]
In our case we filled the json via javascript, which can be a little confusing when it comes with dealing with single/double quotes, from what I've heard.
What's been said before in this and other posts, like including the 'Accept' and 'Content-Type' headers, applies too.
Hope t'helps.
I managed out how to make it works. Tell me in case I am wrong.
I used only one way to serialize/deserialize: I removed all annotations regarding this (#JSONSerialize and #JSONDeserialize) and registered Serializers and Deserializers in CustomObjectMapper class.
I didn't find an article explaining this behaviour but I resolved in this way. Hope it's useful.
I had the same problem. I had to follow these steps to resolve the issue:
1. Make sure you have the following dependencies:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson-version}</version> // 2.4.3
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-version}</version> // 2.4.3
</dependency>
2. Create the following filter:
public class CORSFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String origin = request.getHeader("origin");
origin = (origin == null || origin.equals("")) ? "null" : origin;
response.addHeader("Access-Control-Allow-Origin", origin);
response.addHeader("Access-Control-Allow-Methods", "POST, GET, PUT, UPDATE, DELETE, OPTIONS");
response.addHeader("Access-Control-Allow-Credentials", "true");
response.addHeader("Access-Control-Allow-Headers",
"Authorization, origin, content-type, accept, x-requested-with");
filterChain.doFilter(request, response);
}
}
3. Apply the above filter for the requests in web.xml
<filter>
<filter-name>corsFilter</filter-name>
<filter-class>com.your.package.CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>corsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
I hope this is useful to somebody.
Spring boot + spring mvn
with issue
#PostMapping("/addDonation")
public String addDonation(#RequestBody DonatorDTO donatorDTO) {
with solution
#RequestMapping(value = "/addDonation", method = RequestMethod.POST)
#ResponseBody
public GenericResponse addDonation(final DonatorDTO donatorDTO, final HttpServletRequest request){
I resolved this issue by adding jackson-json data binding to my pom.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.3</version>
</dependency>
In your Model Class add a json property annotation, also have a default constructor
#JsonProperty("user_name")
private String userName;
#JsonProperty("first_name")
private String firstName;
#JsonProperty("last_name")
private String lastName;
I had the same issue. adding
<mvc:annotation-driven />
<mvc:default-servlet-handler />
to the spring-xml solved it
In addition to setting the Accept and Content-Type headers, and making sure they match the consumes/produces settings for your controller method, you may also want to look at the JSON structure and making sure there are no issues with marshalling and unmarshalling. In my case the content type was OK, but there were issues with mapping JSON requests to my request/response model. Normally the controller should return a 400 error rather than a 415 error, but in my case the error code was 415. I debugged the issue by adding a test class where I used an ObjectMapper to read my JSON into the request model object. The ObjectMapper choked on the request, and gave me helpful errors that helped me fix my model class definition.
1.a. Add following in applicationContext-mvc.xml
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc
add jackson library

Resources