i configure my messageconverter as Jackson's then
class Foo{int x; int y}
and in controller
#ResponseBody
public Foo method(){
return new Foo(3,4)
}
from that i m expecting to return a JSON string {x:'3',y:'4'} from server without any other configuration. but getting 404 error response to my ajax request
If the method is annotated with #ResponseBody, the return type is written to the response HTTP body. The return value will be converted to the declared method argument type using HttpMessageConverters.
Am I wrong ? or should I convert my response Object to Json string myself using serializer and then returning that string as response.(I could make string responses correctly) or should I make some other configurations ? like adding annotations for class Foo
here is my conf.xml
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jacksonMessageConverter"/>
</list>
</property>
You need the following:
Set annotation-driven programming model: put <mvc:annotation-driven /> in spring.xml
Place jaskson jar (Maven artifactId is org.codehaus.jackson:jackson-mapper-asl) in classpath.
Use as the following:
#RequestMapping(method = { RequestMethod.GET, RequestMethod.POST })
public #ResponseBody Foo method(#Valid Request request, BindingResult result){
return new Foo(3,4)
}
This works for me.
Please note, that
#ResponseBody is applied to return type, not to the method definition.
You need #RequestMapping annotation, so that Spring will detect it.
This worked for me:
#RequestMapping(value = "{p_LocationId}.json", method = RequestMethod.GET)
protected void getLocationAsJson(#PathVariable("p_LocationId") Integer p_LocationId,
#RequestParam("cid") Integer p_CustomerId, HttpServletResponse response) {
MappingJacksonHttpMessageConverter jsonConverter =
new MappingJacksonHttpMessageConverter();
Location requestedLocation = new Location(p_LocationId);
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
}
}
}
Note that the method doesn't return anything: MappingJacksonHttpMessageConverter#write() does the magic.
The MessageConverter interface http://static.springsource.org/spring/docs/3.0.x/javadoc-api/ defines a getSupportedMediaTypes() method, which in case of the MappingJacksonMessageCoverter returns application/json
public MappingJacksonHttpMessageConverter() {
super(new MediaType("application", "json", DEFAULT_CHARSET));
}
I assume a Accept: application/json request header is missing.
A HTTP 404 error just means that the resource cannot be found. That can have 2 causes:
Request URL is wrong (client side error or wrong URL in given link/button).
Resource is not there where you expect it is (server side error).
To fix 1, ensure you're using or providing the correct request URL (casesensitive!). To fix 2, check the server startup logs for any startup errors and fix them accordingly.
This all goes beyond the as far posted code and information.
I found that I need jackson-core-asl.jar too, not only jackson-mapper-asl.jar
This is just a guess, but by default Jackson only auto-detects public fields (and public getters; but all setters regardless of visibility). It is possible to configure this (with version 1.5) to also auto-detect private fields if that is desired (see here for details).
I guess that 404 is not related to your HttpMessageConverter. I had same 404-issue and the reason was that I forgot that only requests matching <url-pattern> are sent to DispatcherServlet (I changed request mapping from *.do to *.json). Maybe this is your case also.
In addition to the answers here..
if you are using jquery on the client side, this worked for me:
Java:
#RequestMapping(value = "/ajax/search/sync")
public ModelAndView sync(#RequestBody Foo json) {
Jquery (you need to include Douglas Crockford's json2.js to have the JSON.stringify function):
$.ajax({
type: "post",
url: "sync", //your valid url
contentType: "application/json", //this is required for spring 3 - ajax to work (at least for me)
data: JSON.stringify(jsonobject), //json object or array of json objects
success: function(result) {
//do nothing
},
error: function(){
alert('failure');
}
});
Related
I have a Spring-Boot (v2.0.2) application with a RestController with 2 methods which only differ by the Accept header. A simplified version of the code is this:
#RestController
#RequestMapping("/myapp")
public class FooController {
#GetMapping(value = "/foo/{id}", headers = "Accept=application/json", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> fooJson(#PathVariable id) {
return foo(pageId, true);
}
#GetMapping(value = "/foo/{id}", headers = "Accept=application/ld+json", produces = "application/ld+json;charset=UTF-8")
public ResponseEntity<String> fooJsonLd(#PathVariable id) {
return foo(pageId, false);
}
private ResponseEntity<String> foo(String id, boolean isJson) {
String result = generateBasicResponse(id);
if (isJson) {
return result
}
return addJsonLdContext(result);
}
This works fine. If we sent a request with accept header such as application/json;q=0.5,application/ld+json;q=0.6 for example it will return a json-ld response as it should.
My problem is that if we sent a request with no accept header, an empty accept header or a wildcard */* then it will by default always return a json response whereas I want the default response to be json-ld.
I've tried various things to make the json-ld request mapping take priority over the json one:
Reversing the order in which the mappings are declared.
Adding an #Order annotation to both methods (with value 1 for json-ld and value 2 for the json method)
Creating different classes and putting the #Order annotation at class-level
Adding Accept=*/* as a second accept header to the json-ld mapping does work in giving it preference but has the unwanted side-affect that all accept headers are accepted, even unsupported types as application/xml for example.
The only solution I can think of is creating one request-mapping method that accepts both headers and then processing the accept header ourselves, but I don't really like that solution. Is there a better, easier way to give preference to json-ld?
After some more searching this question on configuring custom MediaTypes pointed me in the right direction.
The WebMvcConfigurerAdapter (Spring 3 or 4) or WebMvcConfigurer (Spring 5) allows you to set a default mediatype like this:
public static final String MEDIA_TYPE_JSONLD = "application/ld+json";
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.valueOf(MEDIA_TYPE_JSONLD));
}
}
This works great for requests with no or an empty accept header, as well as accept: */*. However when you combine an unsupported type with the wildcard, for example accept: */*,text/plain it will return json instead of json-ld!? I suspect this is a bug in Spring.
I solved the issue using the consumes in the #GetMapping annotation. According to the official documentation:
The format is a single media type or a sequence of media types, with a request only mapped if the Content-Type matches one of these media types. Expressions can be negated by using the "!" operator, as in "!text/plain", which matches all requests with a Content-Type other than "text/plain".
In the solution bellow, note that I've added the consumes array to the normal json request mapping, making the client only be able to use the json endpoint if it have the correct Content-Type. Other requests go to the ld+json endpoint.
#GetMapping(value = "/json", headers = "Accept=application/json", consumes = {"application/json"})
#ResponseBody
public String testJson() {
return "{\"type\":\"json\"}";
}
#GetMapping(value = "/json", headers = "Accept=application/ld+json")
#ResponseBody
public String textLDJson() {
return "{\"type\":\"ld\"}";
}
I want to know why spring mvc transform [""] to [null] when I use PostMan to test my API.
here is my controller:
#RequestMapping(value = "", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
public #ResponseBody ResponseEntity<Object> participateRstActivities(
HttpServletRequest request, #RequestBody RstActivityFrom rstForm)
throws ServiceException {
log.info("list size:{}, frist object:{}",rstForm.getRestaurant_ids().size(), rstForm.getRestaurant_ids().get(0));
}
here is my java bean:
public class RstActivityFrom {
private List<Integer> restaurant_ids;
private int activity_id;
// omit getter & setter
}
here is my request body when I use postman to test my api:
{
"restaurant_ids":[""],
"activity_id":119129
}
and the log in controller print :
list size:1, frist object:null.
this problem makes me feel confuse, I want to know why. Thanks
Since restaurant_ids is a List and not String, Change your JSON for restaurant_ids:
{
"restaurant_ids":[],
"activity_id":119129
}
If you don't want to allow an empty String value for objects mapped from your JSON, you can look into setting the Jackson's ObjectMapper Features as described here:
https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html
The Java API for Jackson's DeserializationConfig.Feature(s) can be found here:
http://fasterxml.github.io/jackson-core/javadoc/1.9/org/codehaus/jackson/map/DeserializationConfig.Feature.html
I have about fifty controllers that use #ResponseBody annotation.
Like this:
#RequestMapping(value = "/someUrl.controller", method = RequestMethod.GET)
public #ResponseBody Object getObject(#RequestParam("id") Long id) {
Object object = provider.getObject(id);
return object;
}
Some times getObject method return null. The issues is that on client side I get empty response instead of null.
In the initial implementation we have custom JsonView object that works as wrapper without #ResponseBody annotation.
Like this:
#RequestMapping(value = "/someUrl.controller", method = RequestMethod.GET)
public JsonView<Object> getObject(#RequestParam("id") Long id) {
Object object = provider.getObject(id);
return new JsonView(object);
}
So it was working fine.
I have found some solution at How do you override the null serializer in Jackson 2.0? but unfortunatly it works only for fields in the POJOs.
Have you any ideas how in it can be handled?
Thanks in advance!
This is not a trivial problem to solve.
Spring has a common pattern in which if a handler method returns null, it is meant to indicate that the handler has already dealt with producing and writing the appropriate response content and that no further action is necessary on the front.
Spring has applied this pattern in its RequestResponseBodyMethodProcesser (the HandlerMethodReturnValueHandler implementation for #ResponseBody). It checks if the return value is null. It sets the request as handled. If the return value is not null, it attempts to serialize it with an appropriate HttpMessageConverter.
One option is to create your own #ResponseBodyNull annotation and a corresponding HandlerMethodReturnValueHandler which does the same except also handles null. Note that you can't necessarily reuse the code from RequestResponseBodyMethodProcess because some HttpMessageConverters would fail trying to use null.
Another similar option is to override RequestResponseBodyMethodProcessor to accept null (with the limitations stated above) and register it explicitly with your RequestMappingHandlerMapping, overwriting the default HandlerMethodReturnValueHandlers. You have to do this carefully (ie. register the same ones), unless you want to lose functionality.
The better solution, IMO, would be to not deal with null in the response body. If getObject doesn't return anything, that seems like a 404 to me. Set the appropriate response code and voila!
You can always inject the HttpServletResponse into your handler method and do something like
Object object = getObject(..);
if (object == null) {
response.getWriter().print("null");
// also set the content type to application/json
}
return object;
Assuming you knew that this had to be serialized to JSON.
You can return a ResponseEntity and specify the HTTP status to an error when the object is null:
#RequestMapping(value = "/someUrl.controller", method = RequestMethod.GET)
public ResponseEntity<Object> getObject(#RequestParam("id") Long id) {
Object object = provider.getObject(id);
if (object == null ) {
return new ResponseEntity<Object> (HttpStatus.BAD_REQUEST); // Or any other error status
} else {
return new ResponseEntity<Object> (object, HttpStatus.OK);
}
}
In this way your client will be able to know when the object is null checking the response status.
If you actually need the null value returned you can configure Jackson to serialize it (code from tkuty):
<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>
I hope this help you.
Firstly, I advise you should not write like this:
#RequestMapping(value = "/someUrl.controller", method = RequestMethod.GET)
public #ResponseBody Object getObject(#RequestParam("id") Long id) {
/*
*/
}
Make it as standard code. try this:
#RequestMapping(value = "/someUrl.controller", method = RequestMethod.GET)
#ResponseBody
public Object getObject(#RequestParam("id") Long id) {
Object object = provider.getObject(id);
return object;
}
I have experience with quality scanner, this way helps you avoid error found by scanner. About your problem, you can try Transformer or addScala() to return a POJO instead. I faced to this trouble, and made a deal! Good luck.
I have a REST Webservice returning an int via #responseBody and I want this response to be in XML, and I don't know how to achieve that despite many tries.
My controller is as follow:
#RequestMapping(value = "/UserByAppli", method = RequestMethod.GET)
#ResponseBody
public List<Application> getNbUserByAppli()
{
return this.DAO.getNbUserByAppli();
}
And my application Object:
#Component
#XmlRootElement(name="Application")
#XmlAccessorType(XmlAccessType.FIELD)
public class Application
{
#XmlElement(name="Nom")
private String name;
#XmlElement(name="NbUtilisateurs")
private int nbUsers;
public Application()
{
}
...
}
It always returns application/json, and when I specify the header "Accept=application/xml" I get a 406 response code with org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation in Spring logs.
An explanation or a search direction would be appreciated...
Make sure you have JAXB2 in your classpath and have registered the appropriate message converter and pass the Accept: application/xml header. Also, like M. Deinum suggested, for the marshalling to work, you also need to wrap the <Application /> elements in another element <Applications />.
Background
I have an error message class:
#XmlRootElement
public class ErrorMessage {
private String message;
public ErrorMessage() {
}
public ErrorMessage(String message) {
this.message = message;
}
public String getError() {
return message;
}
public void setError(String message) {
this.message = message;
}
}
This class has been assigned as a return value to an #ExceptionHandler in my Spring MVC REST controller:
#ExceptionHandler
#ResponseStatus(HttpStatus.NOT_FOUND)
#ResponseBody
ErrorMessage handleException(RuntimeException e) {
return new ErrorMessage("something went wrong");
}
Whenever the client triggers a RuntimeException after issuing a request with application/json as a Accept header, it receives a response with the correct status code and a matching JSON body:
{"error":"something went wrong"}
Alternatively, an XML body is received if the Accept header is application/xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<errorMessage><error>something went wrong</error></errorMessage>
Problem
Now, I would like to generify this solution by implementing a HandlerExceptionResolver instead so I don't have to copy / paste the #ExceptionHandler to every controller (or create a common "parent controller" that the other controllers can extend).
However, the AbstractHandlerExceptionResolver.doResolveException() method returns a ModelAndView and not my propriety ErrorMessage, so I tried the following:
public class RuntimeExceptionHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
#Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (ex instanceof RuntimeException) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
ModelAndView mav = new ModelAndView();
mav.addObject("error", "something went wrong");
return mav;
}
return null;
}
}
When debugging, I can see that the mav.addObject() method is called. The response on the client side has the expected status code, but the content type is text/html with html in the body, rather than the JSON or XLM content that was specified by the Accept header in the original request.
(Side note, the actual exception, response code and text message in the example above is not important, they just serve as simple example.)
Spring version: 3.1.1.RELEASE
To get #ExceptionHandlers to use content negotiation, the AnnotationMethodHandlerExceptionResolver has a setMessageConverters() method which must be provided with the message converters, e.g.:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver">
<property name="messageConverters">
<list>
<ref bean="xmlConverter"/>
<ref bean="jsonConverter"/>
</list>
</property>
</bean>
But since you're using a custom method, you'll probably have to implement this functionality yourself. The simplest way is probably to Ctrl-C Ctrl-V from the AnnotationMethodHandlerExceptionResolver source, specifically the handleResponseBody() method.
I have spent some time investigating the matter and I have written a blog post in which I present a solution to the problem.
Update: If you are using Spring 3.2, you can take advantage of the #ControllerAdvice annotation. More details can be found in my second blog post.