SyntaxHighlighter

Showing posts with label JSON. Show all posts
Showing posts with label JSON. Show all posts

Wednesday, April 17, 2013

Spring WebMVC - returning XML instead of JSON using XStream

Spring WebMVC - returning XML instead of JSON using XStream

here's a little set of code i put together in response to a stackoverflow question (http://stackoverflow.com/questions/16060973/configuring-spring-mvc-rest-to-return-only-xml/160621420)

here's the entity that i'm going to expose

package de.incompleteco.spring.domain;

import java.io.Serializable;

public class SimpleEntity implements Serializable {

 private Long id;
 
 private String stuff;
 
 public SimpleEntity() {
  this.id = System.currentTimeMillis();
 }

 public Long getId() {
  return id;
 }

 public void setId(Long id) {
  this.id = id;
 }

 public String getStuff() {
  return stuff;
 }

 public void setStuff(String stuff) {
  this.stuff = stuff;
 }
 
 
 
}


here's the very simple controller

package de.incompleteco.spring.web.controller;

import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import de.incompleteco.spring.domain.SimpleEntity;

@Controller
@RequestMapping("/simpleEntity")
public class SimpleRestController {

 
 @RequestMapping(method=RequestMethod.GET,produces=MediaType.APPLICATION_XML_VALUE)
 public @ResponseBody SimpleEntity get() {
  return new SimpleEntity();
 }
 
}



here's the xml way of wiring it up to use XStream as the XML Streaming engine


 
  
   
    
    
    
     
      #{T(org.springframework.http.MediaType).APPLICATION_XML_VALUE}
     
    
   
  
 
 
 

 





here's the @Configuration way to do the same

package de.incompleteco.spring.web;

import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.xml.MarshallingHttpMessageConverter;
import org.springframework.oxm.support.AbstractMarshaller;
import org.springframework.oxm.xstream.XStreamMarshaller;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
@EnableWebMvc
@ComponentScan("de.incompleteco.spring.web")
public class SimpleConfiguration extends WebMvcConfigurerAdapter {
 
 private XStreamMarshaller marshaller;
 
 @Override
 public void configureMessageConverters(List> converters) {
  converters.add(getHttpMessageConverter());
  super.configureMessageConverters(converters);
  
 }

 public HttpMessageConverter getHttpMessageConverter() {
  MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter();
  converter.setMarshaller(marshaller());
  converter.setUnmarshaller(marshaller());
  return converter;
 }

 @Bean
 public AbstractMarshaller marshaller() {
  if (marshaller == null) {
   return new XStreamMarshaller();
  } else {
   return marshaller;
  }
 }
 
}



and here's the tests for the code.
package de.incompleteco.spring.web.controller;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({"classpath:/META-INF/spring/servlet-context.xml"})
public class SimpleRestControllerTest {

 @Resource
 private WebApplicationContext context;
 
 @Test
 public void testGet() throws Exception {
  MockMvc mvc = webAppContextSetup(context).build();
  //test
  MvcResult result = mvc.perform(get("/simpleEntity").contentType(MediaType.TEXT_HTML)).andExpect(status().isOk()).andReturn();
  //now show the context
  System.out.println(result.getResponse().getContentAsString());
 }

}



and
package de.incompleteco.spring.web.controller;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.web.context.WebApplicationContext;

import de.incompleteco.spring.web.SimpleConfiguration;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes=SimpleConfiguration.class)
public class ConfigSimpleRestControllerTest {

 @Resource
 private WebApplicationContext context;
 
 @Test
 public void testGet() throws Exception {
  MockMvc mvc = webAppContextSetup(context).build();
  //test
  MvcResult result = mvc.perform(get("/simpleEntity").contentType(MediaType.TEXT_HTML)).andExpect(status().isOk()).andReturn();
  //now show the context
  System.out.println(result.getResponse().getContentAsString());
 }

}


one thing to note: you'll need to comment out/keep the @Configuration separate from the XML - the XML scans and hits the @Configuration annotation and tries to an appcontext inside and appcontext (and dies)

and here's the pom too...

  4.0.0
  de.incompleteco.spring.webmvc
  spring-webmvc-rest-xml
  0.0.1-SNAPSHOT
  war
  spring-webmvc-rest-xml
  
   
     
       org.apache.maven.plugins
       maven-compiler-plugin
       
         1.7
         1.7
       
     
      
    
  
   
    org.springframework
    spring-webmvc
    3.2.1.RELEASE
   
   
    org.springframework
    spring-test
    3.2.1.RELEASE
    test
   
   
    junit
    junit
    4.10
    test
   
   
    javax.servlet
    javax.servlet-api
    3.0.1
    provided
   
   
    org.springframework
    spring-web
    3.2.1.RELEASE
   
   
    log4j
    log4j
    1.2.17
    test
   
   
    org.springframework
    spring-oxm
    3.2.1.RELEASE
   
   
    com.thoughtworks.xstream
    xstream
    1.4.4
   
  

Sunday, January 6, 2013

Spring WebMVC 3.2 and Validation

Spring WebMVC 3.2 and Validation

Spring 3.2 WebMVC has a neat new feature regarding validation and error handling.  Let's say we have the following use case;
- objects are POSTed as JSON
- objects are @Valid on arrival
- any validation errors are returned as parsed JSON responses inside the body of the reply so that the client can process

so, first we have our normal request processor

package de.incompleteco.spring.web.controller;

import javax.validation.Valid;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import de.incompleteco.spring.web.domain.SimpleEntity;

@Controller
public class ValidatorController {

 @RequestMapping(value="/simpleEntity",method=RequestMethod.POST)
 public SimpleEntity process(@Valid SimpleEntity simpleEntity) throws Exception {
  System.out.println("got it here");
  simpleEntity.setProcessed("true");
  return simpleEntity;
 }
 
}

and here's the entity

package de.incompleteco.spring.web.domain;

import java.io.Serializable;

import javax.validation.constraints.NotNull;

public class SimpleEntity implements Serializable {

 private static final long serialVersionUID = 1L;

 @NotNull
 private Long id;
 
 private String processed;

 public Long getId() {
  return id;
 }

 public void setId(Long id) {
  this.id = id;
 }

 public String getProcessed() {
  return processed;
 }

 public void setProcessed(String processed) {
  this.processed = processed;
 }
 
 
 
}

so if we pass it in without the id set, we'll get a BindingException.

with Spring 3.2 WebMVC, here's how we can catch all the BindingExceptions across the application and return them.  we want to return a 400 status code and the ObjectErrors in the body of the response.

package de.incompleteco.spring.web.controller;

import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import de.incompleteco.spring.web.domain.ErrorEntity;

@ControllerAdvice
public class ValidatorAdvice {

 @ExceptionHandler(BindException.class)
 public @ResponseBody ErrorEntity handleException(BindException exception,HttpServletResponse response) {
  ErrorEntity entity = new ErrorEntity();
  entity.setErrors(exception.getBindingResult().getAllErrors());
  //set the response status
  response.setStatus(HttpStatus.BAD_REQUEST.value());
  //return
  return entity;
  
 }
 
}


and here's our "payload" entity

package de.incompleteco.spring.web.domain;

import java.io.Serializable;
import java.util.List;

import org.springframework.validation.ObjectError;

public class ErrorEntity implements Serializable {

 private List< ObjectError > errors;

 public List< ObjectError > getErrors() {
  return errors;
 }

 public void setErrors(List< ObjectError > errors) {
  this.errors = errors;
 }
 
}


what our result will be is the ErrorEntity processed via Jackson (JSON) and the status code set to 400.

by the way, our Spring configuration is this;




 
 
 



don't ya love 3.2!

Saturday, October 27, 2012

Spring Integration, Content-Length and WebSphere

Spring Integration, Content-Length and WebSphere


one little annoying thing i discovered the other day was that WebSphere (either app server or the http server) truncates the outbound response to the content-length value.  so, if you're content-length value is less than your content, you'll get a truncated body content.  nice...

this becomes a little bit of a problem when using Spring Integration 2.1.x out of the box.  if you're using the http-inbound-gateway, and you don't set which request headers to map, it will map all of them, including content-length.  This header then stays on the message (unless manipulated later) and is passed out, with the value set by the original request.  

the fix for this is straight-forward, set the mapped-request-headers value.  that way, only the specific header names will be set.  otherwise, you can enrich the headers on the way out, but that becomes a little more tricky when specifying the content length.


Sunday, September 23, 2012

Spring @Valid and JSON

Spring @Valid and JSON


a neat feature of Spring 3.1 is the ability to set @ExceptionHandler for types thrown by controller methods.  in a JSON scenario, we may wish to format the error back to the client and the @ExceptionHandler allows us to do exactly that.

here's how to do it;

let's create the entities, including an error encapsulating entity





now create an abstract error controller (for reuse design)



now let's use that in a controller



and, of course, the wiring





Wednesday, August 29, 2012

Spring MVC - Internet Explorer 7 and JSON

Spring MVC - Internet Explorer 7 and JSON

here's a little "trick" i found out with IE 7... if you're ever in the situation where you need to send JSON directly to the client (say, in an IFrame scenario), IE7 will misunderstand the application/json type and pop up the download dialog.  so, to counter this in Spring MVC 3, add the following to the annotations for the MessageConverter...



Tuesday, August 7, 2012

Spring Integration JSON Example

Spring Integration JSON Example


a colleague created this great snippet for RESTFul requests