I have a backbone.js client app and Spring on the app tier. In Backbone I'm using a Collection of 'ServerConfig' objects... the URL of the backbone collection points to a spring endpoint.
The 'GET' request returns an ArrayList of a POJO called 'ServerRuntimeConfig' which ultimately ends up returning a JSON response with the following response body:
[{"useIUX":true,"serverDomainName":"localhost","selected":true}]
The 'PUT' request, which is called when I do a 'sync' on the collection sends the following JSON request with the below request body (same as the GET):
[{"useIUX":true,"serverDomainName":"localhost","selected":true}]
The problem is that I can't get spring to automatically map the JSON collection into a collection of POJOs on the PUT... so I've had to do it manually.
Here is the GET end point
#RequestMapping(value = "/runtimeConfigs",
method = RequestMethod.GET,
produces = "application/json")
#ResponseBody
public ArrayList<ServerRuntimeConfig> handleConfigRequest(
HttpServletRequest request,
HttpServletResponse response) {
// first grab all of the hostnames from the properties file
ArrayList<String> allServersDomains = new ArrayList();
List<String> servers = Arrays.asList(
StringUtils.tokenizeToStringArray(
this.getAppProperties().get("serverList"), ","));
allServersDomains.addAll(servers);
MyTTJMXClient jmxClient = new MyTTJMXClient();
return jmxClient.readRemoteConfigurations(allServersDomains);
}
Here is the PUT endpoint
#RequestMapping(value = "/runtimeConfigs",
method = RequestMethod.PUT,
consumes = "application/json",
produces = "application/json")
#ResponseBody
public ArrayList<ServerRuntimeConfig> handleConfigUpdate(
#RequestBody String body,
HttpServletRequest request,
HttpServletResponse response) {
String jsonSource = body;
ObjectMapper mapper = new ObjectMapper();
ArrayList<ServerRuntimeConfig> serverConfigs = new ArrayList();
try {
serverConfigs = mapper.readValue(jsonSource,
new TypeReference<ArrayList<ServerRuntimeConfig>>() { } );
} catch (IOException e) {
e.printStackTrace();
}
MyTTJMXClient jmxClient;
jmxClient = new MyTTJMXClient();
return jmxClient.setRemoteConfigurations(serverConfigs);
}
Is there a way to avoid the manual Jackson mapping I'm doing in the PUT endpoint? I tried the following endpoint signature but serverConfigs is always null
#RequestMapping(value = "/runtimeConfigs",
method = RequestMethod.PUT,
consumes = "application/json",
produces = "application/json")
#ResponseBody
public ArrayList<ServerRuntimeConfig> handleConfigUpdate(
ArrayList<ServerRuntimeConfig> serverConfigs,
HttpServletRequest request,
HttpServletResponse response) {
Again, the manual Jackson mapping does the trick, but it's not using the awesomeness that is Spring.
Has anyone else out there built a Spring end point that automatically maps Backbone/JSON collections to POJOs?
Assuming the mapping you've used in your PUT method works,
Try to change your method signature from:
#ResponseBody
public ArrayList<ServerRuntimeConfig> handleConfigUpdate(
ArrayList<ServerRuntimeConfig> serverConfigs,
HttpServletRequest request,
HttpServletResponse response) {
to:
#ResponseBody
public ArrayList<ServerRuntimeConfig> handleConfigUpdate(
#RequestBody ArrayList<ServerRuntimeConfig> serverConfigs,
HttpServletRequest request,
HttpServletResponse response) {
The #RequestBody annotation is the important part here. It will automatically use spring's converter (in this case, jackson) to populate the variable.
Related
In a Spring Boot controller, I am receiving json and want to "forward" it without any processing:
#RequestMapping(value = "/forward", method = RequestMethod.POST)
public void abc(#RequestBody GeneralJsonRepresentation json, HttpServletRequest request) {
restTemplate.postForEntity(endpoint, json, Object.class)
}
Is it possible to accomplish this, for instance with an implementation of GeneralJsonRepresentation, assuming the controller has no knowledge of the json format and that the received content type is application/json?
You may not even need the GeneralJsonRepresentation if you just use a String.
I created a small working snippet:
#RequestMapping(path="/forward", method = RequestMethod.POST)
public ResponseEntity<String> forward(#RequestBody String postData) {
// maybe needed configuration
final RestTemplate restTemplate = new RestTemplateBuilder().basicAuthorization("user", "password").build();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(postData, headers);
final String targetUrl = "http://targethost/endpoint";
final ResponseEntity<String> response = restTemplate.postForEntity(targetUrl, entity, String.class);
return ResponseEntity.created(...).build();
}
I have one controller in an app that returns JSON data, like so:
#RequestMapping(value = "/{number}", method = RequestMethod.GET)
#ResponseBody
public String number(
HttpServletRequest request,
HttpServletResponse response,
#PathVariable int number
) {
JSONObject dataObject = new JSONObject();
dataObject.put("firstName", "Sheelten");
dataObject.put("lastName", "Pestay");
JSONArray data = new JSONArray();
data.put(dataObject);
return data.toString();
}
I have another controller on a different app, that I want to receive the JSON data, like so:
#RequestMapping(
value = "/data/test/",
method = RequestMethod.GET
)
#ResponseBody
public String testService(
HttpServletRequest request,
HttpServletResponse response,
Model model
) {
return response.toString();
}
I'm not really sure how I would go about receiving the JSON data into my testService controller method. I've tried googling and using the response object with no luck.
Anyone have an idea how I'd do this?
Found an answer, in case anyone else is ever looking for a solution to this.
If you are using Java with Spring, use the RestTemplate class. See below:
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject("http://yoururl/here", String.class);
The string result will be your JSON string.
Can I handle Jackson UnrecognizedPropertyException for a #RequestBody parameter? How can I configure this?
I'm working on a spring MVC project, and I use jackson as json plugin. Any mis-spell of the field name in a json request will lead to a error page, which should be a json string consist of error message. I'm a newbie to spring, and I think this error handling can be done with some spring configuration, but failed after several attempts. Any help?
Here is my mvc configure:
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver resolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
return bean;
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
My controller:
#RequestMapping(value = "/Login", method = RequestMethod.POST,
consumes="application/json", produces = "application/json")
public #ResponseBody AjaxResponse login(
#RequestBody UserVO user, HttpServletRequest request) {
//do something ...
}
Normal request json is:
{"Username":"123123", "Password":"s3cret"}
But if I send the following request:
{"Username":"123123", "pwd":"s3cret"}
which field name is mis-spell, then Spring catch this UnrecognizedPropertyException, and returned a error page, but I want to catch this exception and return a json string. How can I achieve this?
Use #ExceptionHandler annotation. Some documentation about it: http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
#Controller
public class WebMvcConfig {
#RequestMapping(value = "/Login", method = RequestMethod.POST,
consumes="application/json", produces = "application/json")
public #ResponseBody AjaxResponse login(#RequestBody UserVO user, HttpServletRequest request) {
//do something ...
}
#ExceptionHandler(UnrecognizedPropertyException.class)
public void errorHandler() {
// do something. e.g. customize error response
}
}
I am using Spring Delegating proxy filter to do some validations in Filter class before passing on to Controller. On failed validations i intend to return user defined "ErrorMessage" object in json format, what would be the best way to return json from filter? Is it possible? I tried returning string by writing it in output and it worked fine but i dont seem to able to figure out how to return object and that too in json format
Code Snippet:
application context xml -
<bean class="com.company.rest.ValidationFilter" id="validationFilter" />
ValidationFilter -
public class ValidationFilter implements Filter {
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
final String acceptHeader = httpServletRequest.getHeader("Accept");
if (PAYLOAD_FORMATS.jsonp.getContentType().equalsIgnoreCase(acceptHeader)) {
final OutputStream outputStream = httpServletResponse.getOutputStream();
final GenericResponseWrapper wrapper = new GenericResponseWrapper(httpServletResponse);
//Some validation
if (ifValidationFails) {
httpServletResponse.setStatus(400);
httpServletResponse.addHeader("cause", "Required String parameter is not present.");
//Need to return this object in response
final ExceptionWrapper exception = new ExceptionWrapper();
exception.setErrorMessage("Required String parameter is not present");
//Returning string works fine
outputStream.write(new String("Required String parameter is not present").getBytes());
} else {
chain.doFilter(request, wrapper);
outputStream.write(new String("test" + "(").getBytes());
outputStream.write(wrapper.getData());
outputStream.write(new String(");").getBytes());
}
wrapper.setContentType("text/javascript;charset=UTF-8");
outputStream.close();
} else {
chain.doFilter(request, response);
}
}
#Override
public void destroy() {
}
}
Filters are part of the Servlet API which is quite low level - Spring MVC builds on that standard. You don't get all the goodies that the Spring guys have written to automatically serialize POJOs. Although I imagine it would be possible to manually wire together the necessary components necessary to do it.
I suggest looking at the declartive validation that exists using the #Valid annotation in Spring MVC: http://static.springsource.org/spring/docs/current/spring-framework-reference/html/validation.html#validation-beanvalidation
In my webapp, all my message converters are in place, and if I change getContent below to return a bean/pojo, it returns as " application/json;charset=UTF-8", which is expected, but I now want to serve JSON "as is".
E.g. I have a simple stub web service with which users can PUT a blob of JSON content which is persisted somewhere, and then an equivalent GET call to read it back.
#Controller
public class StubController {
#Autowired
#Qualifier("keyValueStore")
private KVStore kv;
#RequestMapping(value = "/stub/{id}", method = RequestMethod.GET)
public #ResponseBody
String getContent(#PathVariable("id") final String id) {
return kv.get(id);
}
#RequestMapping(value = "/stub/{id}", method = RequestMethod.PUT)
public String putContent(#PathVariable("id") final String id, #RequestBody String body) {
kv.set(id, body);
return "redirect:/stub/"+id;
}
}
However, the getter returns header "Content-Type: text/html;charset=UTF-8" if I call http://host/stub/123.json in the browser. My guess that this is happening is because I'm not returning anything that is "converted" by the Jackson converter, hence the return header isn't modified.
I need it to be application/json -- any ideas what to do? Perhaps an annotation with which I can specify the return headers?
I managed to get around this by adding an HttpServletResponse param to my getContent() method and setting the content type directly.
http://forum.springsource.org/showthread.php?t=97140
#RequestMapping(value = "/stub/{id}", method = RequestMethod.GET)
public #ResponseBody String getContent(#PathVariable("id") final String id, HttpServletResponse response) {
response.setContentType("application/json");
return kv.get(id);
}