Spring 3.2.x: skip the controller method in case of 406 Not Acceptable

440 views Asked by At

So, let's have this simple controller:

@Controller
public class MyController {

    private static final Logger logger = LoggerFactory.getLogger(MyController.class);

    @RequestMapping(value="/entities", method = RequestMethod.GET)
    public @ResponseBody ResultPojo getSomething() {
        logger.info("getSometing");
        return new ResultPojo();
    }
}

...and the following context fragment:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        </bean> 
    </mvc:message-converters>
</mvc:annotation-driven>

Which basically means I want to be able to return nothing but json representations of the result bean, otherwise return 406.

If I send a GET request with accept=application/json, everything works fine, a json representation is returned in the http response with the 200 Ok status.

If I send a GET request with accept=application/xml, 406 is returned.

My problem in the second case is that even though 406 is returned eventually, the getSomething() method is still called (which I can see in the log). While this is no big deal for GET methods, it can cause confusion for POST methods (the resource is altered, but 406 is returned).

Is there a simple way to tell SpringMVC to check the accept header and return 406 before invoking the controller method? Or do I have to develop a custom http SpringMVC interceptor?

2

There are 2 answers

0
eis On BEST ANSWER

Is there a simple way to tell SpringMVC to check the accept header and return 406 before invoking the controller method? Or do I have to develop a custom http SpringMVC interceptor?

the problem is I would have to put the produces clause to every @RequestMapping in every controller. I'd like to set this on an application level.

as far as I know there is no simpler method with SpringMVC. However, using standard JEE filters this is not very hard to do either. Just do something like:

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class YourFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse res,
            FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;

        if (request.getRequestHeader("Accept").contains("application/json")) {
            chain.doFilter(req, res);
        } else {
            ((HttpServletResponse)response).setStatus(SC_NOT_ACCEPTABLE);
        }
    }
    public void init(FilterConfig config) throws ServletException {
         // any startup stuff here if needed
    }
    public void destroy() {
        // add code to release any resource
    }
}

and:

<filter>
    <filter-name>YourFilter</filter-name>
    <filter-class>
        path.to.YourFilter
    </filter-class>
</filter>
<filter-mapping>
    <filter-name>YourFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

(didn't test the code, but it should be about right)

1
Jonas On

Maybe this is what you want:

@RequestMapping(value = "/entities", method = RequestMethod.GET, headers = {"content-type=application/json"})
methodName() {
    ...
}