How To Provide Default Serialization Format With Spring MVC RESTful Services

Spring MVC allows you to serialize objects with @ResponseBody to any format – it has built-in converters for JSON and XML. However, even though your application may support both, you may need a default in case the Accept header is not set. Furthermore (this is a strange case, but we had it), you may want different defaults for different URLs.

As far as I know you can’t choose which of the converters to be used – they are ordered and the first converter in the list gets picked up and serializes the response.

My solution to the problem is trivial, but not the first thing you’d think about. Use a Filter, and in case the client hasn’t supplied an Accept header, wrap the request and make it return the default Accept header, as if the client has set it. That way, whenever spring-mvc invokes the getHeader() method, it will get the desired Accept header.

/**
 * Filter that is needed to set the default Accept header per gateway.
 * According to previous API specifications, each gateway had a default serialization format. Unfortunately these formats differ,
 * and spring can't have a default-per-url. That's why this filter wraps the request in case there is no Accept header and returns 
 * the default one depending on the url
 * 
 * @author bozho
 *
 */
public class DefaultAcceptHeaderSettingFilter implements Filter {

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		String acceptHeader = httpRequest.getHeader("Accept");
		if (StringUtils.isBlank(acceptHeader)) {
			chain.doFilter(new DefaultAcceptHeaderHttpServletRequestWrapper(httpRequest), response);
		} else {
			chain.doFilter(httpRequest, response);
		}
	}

	@Override
	public void destroy() {
	}
	
	public static class DefaultAcceptHeaderHttpServletRequestWrapper extends HttpServletRequestWrapper {

		public DefaultAcceptHeaderHttpServletRequestWrapper(
				HttpServletRequest request) {
			super(request);
		}
		
		@Override
		public String getHeader(String name) {
			if (name.equals("Accept")) {
				if (getRequestURI().contains("/service1")) {
					return "application/json";
				} else if (getRequestURI().contains("/service2")) {
					return "application/xml";
				}
			} 
			return super.getHeader(name);
		}
	}
}

(btw, please let me know if there’s a native spring way to do that)

3 thoughts on “How To Provide Default Serialization Format With Spring MVC RESTful Services”

  1. There are Spring MVC native ways. On an individual method you can use the “produces” attribute of @RequestMapping. It influences the mapping of the request and also the resulting content type of the response. In the absence of an Accept header or in case of ‘Accept=*/*’, the specified producible media type is used.

    In Spring MVC 3.2 (currently RC1) you can also configure a ContentNegotiationStrategy to decide the acceptable media type. There are several built-in strategies and they can be ordered through ContentNeogitationManager. See the what’s new in Spring 3.2 section and this presentation.

  2. Interesting, from the documentation and attempts of a colleague of mine it turned out “produces” limits the type to the specified ones. What happens if you have produces={“application/json”, “application/xml”} ?

    As far as I understood, spring 3.1 rejects anything that is not Accept=application/json or application/xml.
    And if it doesn’t, does it take into account the order of declaration? Or just goes through the list of converters and picks the first one?

    (spring 3.2 is, alas, not an option, the project is in production and upgrade to RC1 is not feasible)

  3. Hi! This is my first visit to your blog! We are a team of volunteers
    and starting a new project in a community in the same niche.
    Your blog provided us valuable information to work on.
    You have done a extraordinary job!

Leave a Reply

Your email address will not be published. Required fields are marked *