How to unmarshall a JSON object to a Java Class? - java

I have Spring REST application that can marshall an object to JSON when a GET request occurs. However what should I do for POST methods. I send a JSON object but it can't unmarshall it into a Java object. Here is a method of my controller:
#RequestMapping(value = "/user", method = RequestMethod.POST)
public void createUser(HttpServletResponse response, #RequestBody User user) {
...
response.setStatus(HttpServletResponse.SC_OK);
}
I send my JSON like that:
var userName = $('#userName').val();
var password = $('#password').val();
var mail = $('#mail').val();
var admin = $("#admin").is(':checked');
var user = {userName: userName, password: password, mail: mail, admin:admin};
$.ajax({
async : false,
type: 'POST',
contentType: 'application/json',
url: '/sfd/user',
data: user,
dataType: 'json',
success: function(data) {
...
},
error: function(data) {
...
}
});
PS:
Here: http://blog.springsource.com/2010/01/25/ajax-simplifications-in-spring-3-0/ it says:
If there are validation errors, a HTTP 400 is returned with the
error messages, otherwise a HTTP 200 is returned.
I have 400 Bad Request Error. Maybe the problem is related to that?
PS2: Should I send User object after I set all its elements? I mean User object has some other attributes, i.e. address but I don't set and send it from client.
PS3: When I debug AbstractHandlerExceptionResolver's resolveException method I see that error. It says 'u' is undefined character(I tested it that 'u' is the first key of JSON => userName's first character).

You should have the following setting in your XML:
<context:annotation-config/>
and then to make sure that jackson jar is in your classpath:
in my projects I prefer to set it as Spring Bean:
it will deserialize your json data to object.
You can read it more about it here:
http://blog.springsource.com/2010/01/25/ajax-simplifications-in-spring-3-0/

In the #RequestMapping set headers to accept application/json, and then try configuring this in your application context
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver" p:order="1">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
</bean>

Related

How to handle 400 error in Spring MVC

I am getting 400 Http response when i am passing the invalid json format,
I would like to return the custom json message instead of this , can any one advise how to do in Spring 4.1 ?
Handling Execption using ControllerAdvice,but it is not working.
#ControllerAdvice
public class GlobalControllerExceptionHandler {
#ExceptionHandler({org.springframework.http.converter.HttpMessageNotReadableException.class})
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public String resolveException() {
return "error";
}
}
spring-config.xml is given below
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
</map>
</property>
<property name="defaultViews">
<list>
<!-- Renders JSON View -->
<bean
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
</bean>
Given below Json request and response from WebSphere application server (7.0).
Request 1: Empty json request : {}
Response Status Code: 400 Bad Request
Response Message : Json request contains invalid data:null
Request 2:Invalid format of Json Request : {"data":,"name":"java"}
Response Status Code: 400 Bad Request
Response or Exception message :
nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unexpected character (',' (code 44)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
at [Source: com.ibm.ws.webcontainer.srt.http.HttpInputStream#8f308f3; line: 5, column: 57]
Similar question like below link
Using Spring MVC, accepting POST requests with bad JSON leads to a default 400 error code server page being returned
You can attempt to map the exception this way. This code will return a 400 status, but you can change the return the same way as is the link you posted
#ExceptionHandler
#ResponseStatus(HttpStatus.BAD_REQUEST)
public void handleJsonMappingException(JsonMappingException ex) {}
Finally i have handle the exception via Servlet Filter with HttpServletRequestWrapper.
Step 1: Add the filter
Step 2: Get the request body from Customize HttpServletRequestWrapper class
Step 3: Convert request body json string to java object using JSON API
Step 4: Chain the request/response
Step 5: Catch exception / and update the HttpServlet Response
Using below reference.
Filter Example
HttpServletRequestWrapper Example
String to Json Object
With the help of this approach i can handle 400/405/415 Http Errors.
You may try this, in your pom.xml add dependency:
<!-- Need this for json to/from object -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.3</version>
</dependency>
this will convert your java objects to JSON automatically when you return them. like you can write a class for response:
public class Response {
private int responseCode;
private String responseMessage;
//as many fields as you like
public Response (int responseCode, String responseMessage) {
this.responseCode = responseCode;
this.responseMessage = responseMessage;
} }
then you can return any java objects and they will be received as JSON,
#RequestMapping(value="/someMethod", method=RequestMethod.POST)
public #ResponseBody Response someMethod(#RequestBody Parameters param) {
return new Response(404, "your error message");
}

How to register a custom Endpoint Mapping in Spring Soap web services

I'm implementing a SOAP client that interacts with some legacy SOAP services.
All of SOAP body requests have the same format, like this:
<soap:Body>
<execute>
<msg>
</msg>
</execute>
</soap:Body>
As content, the msg element supports a list of any XML tags, so I can send any type of element inside msg: orders, customer, contacts, etc..
All of the requests also have the same action name.
Due to the restrictions/aspects above, if I use spring's PayloadRootAnnotationMethodEndpointMapping, since the root element in the soap body is the same for all the requests, each request will fall in the same method of my Endpoint. If I use spring's SoapActionAnnotationMethodEndpointMapping, since the action is the same for every requests, all of them will fall in the same method again.
The only thing I've got different in the requests, is the requesting URI.
It changes by the name of the operation that I'm invoking. Like:
http://host:port/services/Operation1?Wsdl or
http://host:port/services/Operation2?Wsdl
My idea was to have an endpoint for each type of requests. For example: all request related to products would fit into a "ProductsEndpoint".
I wanted to create a custom endpoint mapping that would extend springs AbstractEndpointMapping. In my implementation I would decide which endpoint to call based on the URI.
But how can I register my custom endpoint mapping in the spring's endpoint mapping chain?
Best regards
If anyone has a request like mine explained above, here goes what I've decided to do...
I've create a class MultipleMarshallersPayloadMethodProcessor extending spring org.springframework.ws.server.endpoint.adapter.method.MarshallingPayloadMethodProcessor. This is the class responsible for marshalling and unmarshalling arguments. In this class I've defined a java.util.map that will associate a given URL to a specific Marshaller. If the current URL request isn't a key in the map, it would use the default Marshaller supplied by the MarshallingPayloadMethodProcessor class.
To register the class as a spring bean:
<bean id="marshallingPayloadMethodProcessor"
class="br.com.tim.fiber.middleware.services.server.helpers.MultipleMarshallersPayloadMethodProcessor">
<constructor-arg ref="defaultMarshaller" />
<property name="otherMarshallers">
<map>
<entry key="/Operation1?wsdl" value-ref="operation1Marshaller"></entry>
<entry key="/Operation2?wsdl" value-ref="operation2Marshaller"></entry>
</map>
</property>
</bean>
And an example of a marshaller:
<bean id="operation1Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPaths">
<list>
<value>com.app.dtos.operation1</value>
<value>com.app.dtos.common</value>
</list>
</property>
</bean>
With this setup I was able to marshall and unmarshall any request according to the URL. Then I've used the Facade design pattern to create a SOAP Endpoint with a single method that receives all the requests. That method just inspects the URL and delegates to specific endpoints.
#Endpoint
public class FacadeEndpoint {
private static final String NAMESPACE_URI = "http://my.namespace.com/services";
#Autowired
private RequesEndpointURLExtractor requestUrlExtractor;
#Autowired
private OrdersEndpoint ordersEndpoint;
#SuppressWarnings("unchecked")
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "execute")
#ResponsePayload
public ExecuteResponse dispatch(#RequestPayload Execute request) {
String serviceURL = this.requestUrlExtractor.getCurrentURL();
ExecuteResponse response = null;
if (serviceURL.equals(ServiceRequestsEndpoint.CREATE_ENDPOINT_URI)) {
Operation1DTO serviceRequest = (Operation1DTO) request.getMsg().getAnies().get(0);
}
...
}
The RequestEnpointURLExtractor is just a spring bean that extracts the full URL from the request.
#Component
public class RequesEndpointURLExtractor {
public String getCurrentURL() {
TransportContext ctx = TransportContextHolder.getTransportContext();
HttpServletRequest httpServletRequest = ((HttpServletConnection) ctx.getConnection()).getHttpServletRequest();
String pathInfo = httpServletRequest.getPathInfo();
String queryString = httpServletRequest.getQueryString();
return pathInfo + "?" + queryString;
}
}
I could create a custom annotation that would have a URL associated, and use that annotation on methods that would process the request with the URL configured. That would be a cleaner solution instead of the if/else if ladder that I have.
But, since this was only for a simple mockup server, the if/else if ladder isn't such a big deal.

Spring RestTemplate unable to parse json response

If I parse the response of a POST as string it works perfectly:
ResponseEntity<String> stringResponse = restTemplate.postForEntity(DruidClient.QUERY_HOST + "/druid/v2", query, String.class);
String valueResults = stringResponse.getBody();
DruidValueResult[] results = new ObjectMapper().readValue(valueResults, DruidValueResult[].class);
However, if i tell spring to parse the response directly:
ResponseEntity<DruidValueResult[]> results = restTemplate.postForEntity(DruidClient.QUERY_HOST + "/druid/v2", query, DruidValueResult[].class);
I get the following error:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class [Lcom.dripstat.metricprocessor.druid.DruidValueResult;] and content type [application/smile]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:108)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:788)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:773)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:553)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:506)
at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:361)
Why isn't spring able to parse the resulting json directly?
From SpringSource Blog:
Objects passed to and returned from the methods getForObject(), postForLocation(), and put() and are converted to HTTP requests and from HTTP responses by HttpMessageConverters. Converters for the main mime types and Java types are registered by default, but you can also write your own converter and plug it in the RestTemplate. In the example below, I will show you how that's done.
I suppose the same for postForEntity(), so you may need to add a message converter for your specific mime type since it is not marshelled by default:
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/smile" />
<property name="supportedMediaTypes" value="text/javascript" />
</bean>
</list>
</property>
</bean>

Spring-MVC 406 Not Acceptable instead of JSON Response

I'm trying to return a JSON response with Spring 3.0.6, but I get a 406 response "Not Acceptable", with the description:
"The resource identified by this request is only capable of generating responses with characteristics
not acceptable according to the request "accept" headers ()."
I know that a very similar question has been asked before, but I can't make it work for my project, despite many
tests and I don't understand what I'm doing wrong.
In my Maven pom.xml I've the following:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.8.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.8.5</version>
<scope>compile</scope>
</dependency>
In web.xml I reference webmvc-config.xml, and the log confirms that is loaded.
<servlet>
<servlet-name>mainServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/webmvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
In webmvc-config.xml I've the following:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<mvc:annotation-driven />
My Controller is:
#Controller
public class ClassifiedController {
#RequestMapping(value = "/classified/{idClassified}", headers = "Accept=*/*",
method = RequestMethod.GET)
#ResponseBody
public final Classified getClassified(#PathVariable final int idClassified) {
...
I tried with or without the headers parameter with the same results. If I call the URL
directly with Firefox the Request Headers contain the following (checked with firebug):
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
If I use the following JQuery:
$.ajax({
url: '/classified/38001',
type: 'GET',
dataType: 'json'
});
The following headers are sent:
Accept: application/json, text/javascript, */*; q=0.01
In both cases the result is a 406 error. I don't know what else should I check to make
it work.
UPDATE: I decided to debug through Spring and I found out that Jackson was called correctly and in org.codehaus.jackson.map.ser.StdSerializerProvider the method _findExplicitUntypedSerializer contains the following code:
try {
return _createAndCacheUntypedSerializer(runtimeType, property);
} catch (Exception e) {
return null;
}
This is unfortunate because hides the source of the problem. With the debugger I found out that that exception contained a very descriptive error message:
Conflicting getter definitions for property "reminded":
ClassifiedImpl#isReminded(0 params) vs
ClassifiedImpl#getReminded(0 params)
Now that I see the error message is a silly mistake and easy to fix, but without that it wasn't that obvious. In fact, fixing the problem, leaded to a working serialization.
Add the following in DispatcherServlet-servlet.xml.
<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>
I've stumbled upon the same error (406: content not acceptable) with Spring MVC and #RestController annotation.
The Spring handler:
#RequestMapping(value = "/stuff-acknowledgment/{id}", produces ="application/json;charset=UTF-8", headers="Accept=*")
public Message acknowledgeStuff(#PathVariable("id") String id, #ModelAttribute("ack") AckBean acquittement) {
Observation:
the URI has the form : http://www.host.com/stuff-acknowledgment/{id}
BUT $id has a very particular format: xxxcomplicatedhashxxx.png (or whatever extension you can think of).
Therefore:
Spring MVC interpret the extension and want to produce a result of that same mime type (even if I define it as a path variable), here an "image/png" MIME type even if I tell him to produce JSON. So a 406 exception is thrown.
Fix:
Remove the ".png" extension in the URI, or remove the PathVariable and put it in the body, or add a suffix behind the pathVariable (not tested but should work as well), the point is to avoid a file extension at the end of the URI.
P.S.: I know it doesn't answer the specific problem (with the solution in the update) in the question but I found that SO thread when searching for that problem and post my fix here for the record, hoping it can help someone in the future.
In terms of the MappingJacksonJson processing, you'll need to make sure that the Jackson ObjectMapper supports your object type for serialisation.
I ran into this problem because the objects that I wanted to return as JSON didn't have any getter methods for their properties. Jackson probably needs these. After adding them it worked.
although this thread is a little old...
u need to add the following (maven dependency):
org.codehaus.jacksonjackson-mapper-asl1.9.13

How to match wildcard accept header when using #RequestMapping in Spring framework

I have three resources:
#RequestMapping(value = "sample", method = RequestMethod.GET, headers = "Accept=text/html")
public ResponseEntity<String> sampleResourceHtml()
#RequestMapping(value = "sample", method = RequestMethod.GET, headers = "Accept=application/xml")
public ResponseEntity<String> sampleResourceXml()
#RequestMapping(value = "sample", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseEntity<String> sampleResourceJson()
When a HTTP client accesses the url with Accept=*/* the webapp returns a 404
In this case I want to invoke sampleResourceHtml()
Changing "Accept=text/html" to "Accept=text/html, */*" will make my webapp accept requests with Accept=*/* which is what I want, however it will also accept requests with Accept=foo/bar which is not what I want.
How do I modify my code to return a supported media type for requests containing wildcards without returning an unexpected media type for unsupported requests?
You might find it easier to configure this in your context with the AnnotationMethodHandlerAdapter, so that the Accept header is automatically handled and the conversion done by Spring and not programmatically.
For example, you could use the following configuration:
<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller"/>
<bean id="xmlMessageConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<constructor-arg ref="xstreamMarshaller"/>
</bean>
<bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<util:list>
<ref bean="xmlMessageConverter"/>
<ref bean="jsonHttpMessageConverter"/>
</util:list>
</property>
</bean>
And modify the controller to return the object which Spring will convert to the required type.
#RequestMapping(value = "sample", method = RequestMethod.GET)
public ResponseEntity<String> sampleResource()
Note: You will need the relevant libraries on the classpath.

Categories

Resources