Spring MVC #RequestBody JSON - json

I'm getting an error that the request sent by the client was syntactically incorrect. What is being done wrong? Here is my code:
#Entity
#Table(name = "display")
public class Display {
private String diagonal;
private String aspectRatio;
//getter and setter
}
$.ajax({
type:'POST',
url:'/admin/updateDisplay',
data:{'diagonal':"sss"}
})
#Controller
#RequestMapping(value = "/admin")
public class AdminController {
#RequestMapping(value = "/updateDisplay", method = RequestMethod.POST)
public String updateDisplay(#RequestBody Display display){
System.out.print(display);
return null;
}
}

I think you need to say what the service media type will consume for Spring to know how to unmarshall it. Probably application/json.
#RequestMapping(value = "/updateDisplay", method = {RequestMethod.POST},
consumes = {"application/json"})
Probably some Json library too, like Jackson.

Use the following:
$.ajax({
type:'POST',
url:'/admin/updateDisplay',
data:{"diagonal":"sss","aspectRatio":"0.5"},
contentType: 'application/json',
dataType: 'json',
})
it works.
EDIT
If you are booting up Spring application Context using annotaitons, then your config class must have:
#Override
protected void configureContentNegotiation(
ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false).favorParameter(true)
.parameterName("mediaType").ignoreAcceptHeader(true)
.useJaf(false).defaultContentType(MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML)
.mediaType("json", MediaType.APPLICATION_JSON);
}
And your ajax request must include
contentType: 'application/json',
dataType: 'json',
check the modified ajax call above.
If you are booting up spring application context using XMLs then use the below:
<bean id="contentNegotiationManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false" />
<property name="favorParameter" value="true" />
<property name="parameterName" value="mediaType" />
<property name="ignoreAcceptHeader" value="true"/>
<property name="useJaf" value="false"/>
<property name="defaultContentType" value="application/json" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</map>
</property>
</bean>
For more details on writing RESTFUL webservices with Spring 3.2 see my blog

You must convert the JSON data to string before pass it to Spring MVC. So, here is the solution in your case:
$.ajax({
type:'POST',
url:'/admin/updateDisplay',
data: JSON.stringify({'diagonal':"sss"})
})

You don't need #RequestBody.
With #RequestBody Spring calls a converter that takes the whole request and converts it to an object of the required type. You send your data as application/x-www-form-urlencoded, which is the default of jQuery, and there is no built-in converter for that.
Without #RequestBody, when you send form data, spring creates an empty object and sets the properties based on the data you sent. So in your case Spring would do something like
display = new Display();
display.setDiagonal("sss");
Which, I guess, is what you want.

I don't know if this is your problem too, but with me the value is wrong and caused a error 405, example:
#RequestMapping(value = "/planilha/{id}", method = RequestMethod.PUT)
public String update(#PathVariable("id") String id, #RequestBody String jsonStr) {
BasicDBObject json = ((BasicDBObject) JSON.parse(jsonStr));
PlanilhaDAO dao = new PlanilhaDAO();
BasicDBObject ola = dao.update(id, json);
return ola.toString();
}
#RequestMapping(value = "/planilha/{id}", method = RequestMethod.DELETE)
public String delete(#PathVariable("id") String id) {
PlanilhaDAO dao = new PlanilhaDAO();
BasicDBObject temp = dao.remove(id);
return temp.toString();
}
Needed the change for:
#RequestMapping(value = "/planilha/{id}/**", method = RequestMethod.PUT)
public String update(#PathVariable("id") String id, #RequestBody String jsonStr) {
BasicDBObject json = ((BasicDBObject) JSON.parse(jsonStr));
PlanilhaDAO dao = new PlanilhaDAO();
BasicDBObject ola = dao.update(id, json);
return ola.toString();
}
#RequestMapping(value = "/planilha/{id}", method = RequestMethod.DELETE)
public String delete(#PathVariable("id") String id) {
PlanilhaDAO dao = new PlanilhaDAO();
BasicDBObject temp = dao.remove(id);
return temp.toString();
}

Related

#restcontroller and #responsebody not working in Spring MVC + Thymeleaf

In my spring MVC 4.1.5 application configured to use Thymeleaf 2.1.4 (before it was using JSP and it worked fine), i am unable to return a JSON response.
It always returns a full HTML page weather my request mapping is in a #RestController or if its annotated with #responsebody
Here are the controllers
in an #controller class, i have below mapping
#RequestMapping(value = { "/", "/land", "/login" }, method = RequestMethod.GET)
public String getLogin(Model model, HttpSession session) {
session.setAttribute("login", "none");
System.out.println(programId);
model.addAttribute("signUpForm", new SignUpForm());
return "login";
}
and in #RestController class, below is the post method for same URL
#RequestMapping(value = {"/login" }, method = RequestMethod.POST )
public #ResponseBody HashMap<String, Object> login2(#RequestBody SignUpForm signUpForm, HttpServletRequest request,
HttpServletResponse httpServletResponse, HashMap<String, Object> mo, HttpSession session ) {
User user = userDao.findUserByName(signUpForm.getUserName());
if (user != null && encoder.matches(signUpForm.getPassword(), user.getPassword())&& user.getProgram_id()==3) {/* && user.getProgram_id()==3*/
session.setMaxInactiveInterval(1200);
System.out.println(session.getMaxInactiveInterval()+":"+session.getLastAccessedTime()+":"+session.getCreationTime()+":"+session.getServletContext().getContextPath());
session.setAttribute("login", "success");
mo.put("redirect", "/home");
String ipAddress = request.getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}
session.setAttribute("urip", ipAddress);
return mo;
} else {
mo.put("error", "Login failed. Please check your credentials");
return mo;
}
}
Below is my xml configuration
<context:component-scan base-package="com.company.cardholder" />
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="serializationInclusion">
<value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
</property>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<mvc:default-servlet-handler />
<mvc:interceptors>
<bean class="com.company.cardholder.session.interceptors.URLInterceptor" />
</mvc:interceptors>
<mvc:resources mapping="/resources/**" location="/resources/" />
<bean id="templateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
<property name="prefix" value="/WEB-INF/thymeleaf/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML5" />
<!-- Template cache is set to false (default is true). -->
<property name="cacheable" value="false" />
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
</bean>
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
</bean>
Here is my JSON call
$.ajax({
type: 'POST',
url: $form.attr('action'),
data: JSON.stringify({
userName: $form.find('#userName').val(),
password: $form.find('#password').val(),
}),
contentType: "application/json",
/*dataType: 'json',*/
complete: function(data) {
console.log(data);
if (data.redirect) {
// data.redirect contains the string URL to redirect to
window.location.href = data.redirect;
}else if(data.error){
$messageError.text(data.error);
$messageError.removeClass('hidden');
$messageSuccess.addClass('hidden');
}
}
});
Ok. Here is what i figured out to make it work but i still am not sure of the reason.
public #ResponseBody HashMap<String, Object> login2(#RequestBody SignUpForm signUpForm, HttpServletRequest request,
HttpServletResponse httpServletResponse, HashMap<String, Object> mo, HttpSession session ){
////
}
In the above method signature, i was "injecting" a hashmap. And spring framework default or some unknown config decided to inject a "Binding Result aware Hashmap." I am not sure what difference it would have made. But to fix it, i had to do a
HashMap<String, Object> mo=new HashMap<String, Object>;
inside the method body and remove the injected hashmap.
If anyone is able to understand this behaviour, please explain it. I feel i missed something basic in my awareness of spring framework.

How to consume JSON response with spring restemplate properly?

I have a Spring MVC Rest service that return JSON value, i have 8 row, here is my JSON
[
{"no":"1","date":"2015-03-30","grandtotal":699618,"diskon":699618},
{"no":"2","date":"2015-03-30","grandtotal":1867949,"diskon":1867949},
{"no":"3","date":"2015-03-27","grandtotal":2190909,"diskon":2190909},
{"no":"4","date":"2015-03-26","grandtotal":8616120,"diskon":8616120},
{"no":"5","date":"2015-03-26","grandtotal":1095455,"diskon":1095455},
{"no":"6","date":"2015-03-24","grandtotal":938961,"diskon":938961},
{"no":"7","date":"2015-03-24","grandtotal":5603848,"diskon":5603848},
{"no":"8","date":"2015-03-20","grandtotal":3735899,"diskon":3735899}
]
What i trying.. Here is my controller. SpringRestController.java
Jackson Way :
#RequestMapping(value = "/view", method = RequestMethod.GET)
public String initCreationForm(Map<String, Object> model) {
String url = "http://localhost:8080/SpringServiceJson/view/";
RestTemplate restTemplate = new RestTemplate();
TotalDiscList totaldisc = restTemplate.getForObject(url, TotalDisc.class);
model.put("DiscValue",totaldisc);
return "salesorders/totalDisc";
}
Gson way :
public String initCreationForm(Map<String, Object> model) {
String url = "http://localhost:8080/SpringServiceJson/view/";
Gson gson = new Gson();
Collection<TotalDisc> totaldisc = gson.fromJson(url, PiutangListJson.class);
model.put("DiscValue",totaldisc);
return "salesorders/totalDisc";
}
what i missed here? it always give me this error
"Could not extract response: no suitable HttpMessageConverter found for response type [class [Lorg.springframework.samples.my.model.TotalDiscList;] and content type [application/json]"
Here is my object TotalDiscList.java
public class TotalDiscList {
private String no;
#DateTimeFormat(pattern="dd-MM-yyyy")
private Date date;
private long grandtotal;
private long diskon;
//getter setter skipped
}
i should return List<TotalDiscList> totaldisc = restTemplate.getForObject(url, List<TotalDisc>.class); right?
how i do that properly?
If you have a servlet-context.xml, you can add the message-convertor there, like below :
<beans:bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<beans:property name="messageConverters" ref="jsonMessageConverter" />
</beans:bean>
<beans:bean id="jsonMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
I am doing the same and it works.
Edit
You need to provide a Message converter to your RestTemplate
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
And then try using an array, such as:
TotalDiscList[] totaldisc = restTemplate
.getForObject(url, TotalDiscList[].class);
you can configure a json message converter:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonConverter" />
</list>
</property>
</bean>
and then, you can just annotate your method:
#RequestMapping(value = "/view", method = RequestMethod.POST)
public TotalDiscList[] createDiscList(
#RequestBody TotalDiscList[] object) {
}
which would cause the message converter to try to convert to the given class.

CXF 3.0 and Spring - No message body reader has been found for response Class

I'm writing a JSX-RS based Spring+CXF client for a service that sends a simple response as below.
JSON : Response
{
"message": "Hey Karthik"
}
I have the following configuration in my spring.xml:
<jaxrs:providers>
<bean class="org.apache.cxf.jaxrs.provider.json.JSONProvider">
<property name="dropRootElement" value="true" />
</bean>
</jaxrs:providers>
My entity class looks like this
#XmlRootElement
public class HiModel {
private String message;
public HiModel(){}
.
.
.
}
My JAX-RS client is like this:
#Test
public void getMessage(){
WebClient client = WebClient.create("http://localhost:8182");
client.path("hiService/sayHi/hi");
client.type(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON);
Response r = client.get();
System.out.println(r.readEntity(HiModel.class));
}
I get the error : No message body reader has been found for class com.karthik.model.HiModel, ContentType: application/json
How do I resolve it? There are lots of questions with the method I chose to write as client, but I first need to get this resolved. Please help.
EDIT 1 : I can resolve it by
System.out.println(r.readEntity(String.class));
But, How do I resolve it with the entity as HiModel.class
In case it helps someone. I had similar scenario, and I managed to solve it using Jackson JSON library. Using your example:
WebClient client = WebClient.create("http://localhost:8182/hiService/sayHi/hi");
Response r = client.accept("application/json").get();
MappingJsonFactory factory = new MappingJsonFactory();
JsonParser parser = factory.createJsonParser((InputStream)r.getEntity());
HiModel hiModel= parser.readValueAs(HiModel.class);
Very similar test is actually present within Apache CXF JAX-RS archetype.
Add it to the webclient object.
List<Object> providers = new ArrayList<>();
// add custom providers if any
providers.add(new JacksonJaxbJsonProvider());
WebClient client = WebClient.create(ENDPOINT_ADDRESS,providers);
If you are not using spring to configure cxf then:
1) in Web.xml
<servlet>
<display-name>CXFNonSpringJaxrsServlet</display-name>
<servlet-name>CXFNonSpringJaxrsServlet</servlet-name>
<servlet-class>org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet
</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>
com.jaxrs.JaxRsConfigApplication
</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>CXFNonSpringJaxrsServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
2) On the JaxRsConfigApplication.java
public class JaxRsConfigApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<>();
resources.add(ServicioPistaRest.class);
resources.add(ConsultarDatosSolicitud.class);
return resources;
}
#Override
public Set<Object> getSingletons() {
Set<Object> classes = new HashSet<>();
JacksonJaxbJsonProvider jacksonJaxbJsonProvider = new JacksonJaxbJsonProvider();
classes.add(jacksonJaxbJsonProvider);
return classes;
}
}

Jackson converter and Javax Validation (annotation) not working together

If I use the following configuration then jackson converter works (mvc declaration is last)
<!-- Configure to plugin JSON as request and response in method handler -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonMessageConverter"/>
</list>
</property>
</bean>
<!-- Configure bean to convert JSON to POJO and vice versa -->
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</bean>
<context:component-scan base-package="com.base" />
<mvc:annotation-driven />
If I use this configuration in dispatcher.xml then validation works but conversion does not. (mvc declaration first)
<context:component-scan base-package="com.base" />
<mvc:annotation-driven />
<!-- Configure to plugin JSON as request and response in method handler -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonMessageConverter"/>
</list>
</property>
</bean>
<!-- Configure bean to convert JSON to POJO and vice versa -->
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</bean>
Any help greatly appreciated.
Spring version 4.0.6
I chose the part where validation is working and added this in the code base.
#RequestMapping(value = "url", method = RequestMethod.GET)
protected void getLocationAsJson(#PathVariable("id") Integer id,
#RequestParam("cid") Integer cid, HttpServletResponse response) {
MappingJacksonHttpMessageConverter jsonConverter =
new MappingJacksonHttpMessageConverter();
Location loc= new Location(id);
MediaType jsonMimeType = MediaType.APPLICATION_JSON;
if (jsonConverter.canWrite(requestedLocation.getClass(), jsonMimeType)) {
try {
jsonConverter.write(requestedLocation, jsonMimeType,
new ServletServerHttpResponse(response));
} catch (IOException m_Ioe) {
// TODO: announce this exception somehow
} catch (HttpMessageNotWritableException p_Nwe) {
// TODO: announce this exception somehow
}
}
}
Now the validation works as well as JSON returning.
The method is not returning anything.
RequestMappingHandlerAdapter's xml configuration is bit complicated. The problem with this configuration is, it removes spring default configuration for converters. It is better to use coding version of this configuration. Spring default configuration will be intact this way. Here is sample configurations.
Suggested solution, posted on numerous blogs. But not working in my case.
https://dzone.com/articles/customizing
http://www.java-allandsundry.com/2014/09/customizing-httpmessageconverters-with.html
#Configuration
public class MessageConvertorConfiguration extends WebMvcConfigurationSupport {
#Bean
public MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
Custom360DateFormat dateFormat = new Custom360DateFormat();
dateFormat.setDateFormat(new SimpleDateFormat("MM/dd/yyyy"));
dateFormat.setDateTimeFormat(new SimpleDateFormat("MM/dd/yyyy hh:mm a"));
objectMapper.setDateFormat(dateFormat);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(customJackson2HttpMessageConverter());
super.addDefaultHttpMessageConverters(converters);
}
}
Working Solution
#Configuration
public class MessageConvertorConfiguration {
private MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
Custom360DateFormat dateFormat = new Custom360DateFormat();
dateFormat.setDateFormat(new SimpleDateFormat("MM/dd/yyyy"));
dateFormat.setDateTimeFormat(new SimpleDateFormat("MM/dd/yyyy hh:mm a"));
objectMapper.setDateFormat(dateFormat);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
#Autowired
public void updateJacksonConvertor(RequestMappingHandlerAdapter handlerAdapter) {
//remove default jakson convertor
Iterator<HttpMessageConverter<?>> convertorsIterator = handlerAdapter.getMessageConverters().iterator();
while (convertorsIterator.hasNext()) {
HttpMessageConverter converter = convertorsIterator.next();
if(converter instanceof AbstractJackson2HttpMessageConverter) {
convertorsIterator.remove();
}
}
handlerAdapter.getMessageConverters().add(customJackson2HttpMessageConverter());
}
}

How to get with MappingJacksonHttpMessageConverter a Json with Pascal casing?

Here is the thing... i'm using jtable (jquery) to show some user data. This component needs a json with two fields: Result and Records. In my controller i have a method to return the json:
#RequestMapping(method=RequestMethod.POST, value="/getUsersInJson")
public #ResponseBody String getUsersInJsonHandler(){
ElementsInList<User> users = new ElementsInList<User>();
users.setItems(userService.getUsers());
return users;
}
The class ElementsInList contains two fields: result and records. Result is a string to get the success message and records is a parametrized list which contains in this case a list of users. I get this JSON:
"{"result":"OK","records":[{"username":"john",
But i need this:
"{"Result":"OK","Records":[{"username":"john",...
This is my Mapping:
<!-- Json converter bean -->
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jacksonMessageConverter"/>
</list>
</property>
</bean>
How can i do it? I've checked some posts but have old versions.
I'm using Spring 3, Spring MVC and jQuery.
I solved it by using JsonProperty annotation. You can give the name that jackson will use to build the json field. Here there is an example with jtable (jquery):
public class ElementsInList<T> {
#JsonProperty("Result")
private String result = "OK";
#JsonProperty("Records")
private List<T> records;
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public List<T> getRecords() {
return records;
}
public void setRecords(List<T> records) {
this.records = records;
}
}
The result json is this: {"Result":"OK","Records":[{"roleName":"admin"...
But there is more about this annotations. Check the api for more info: http://fasterxml.github.io/jackson-annotations/javadoc/2.1.0/com/fasterxml/jackson/annotation/package-summary.html