4

My goal is to merge/minify all css files and return the result as String.

Here's my Spring test method :

@RequestMapping(value = "/stylesheet.css", method = RequestMethod.GET, produces = "text/css")
@ResponseBody
public void css(HttpServletResponse response) {
    File path = new File(servletContext.getRealPath("/WEB-INF/includes/css/"));

    File[] files = path.listFiles(...);

    for (File file : files) {
        InputStream is = new FileInputStream(file);
        IOUtils.copy(is, response.getOutputStream());
        response.flushBuffer();

        is.close();
    }
}

This is working with Chrome, Firefox and Safari but not with IE and Opera.

After some checks in the inspectors, the URL https://host/project/stylesheet.css is loading in each browsers. I can see the content but it does not seem to be recognized as text/css.

Also, even with produces = "text/css", I can not see the content-type http header in all browsers.

Error log in IE :

CSS ignored because of mime type incompatibility

Does anyone know how to correctly do this?

Working code :

@RequestMapping(value = "/stylesheet.css", method = RequestMethod.GET)
public ResponseEntity<Void> css(HttpServletResponse response) {
    response.setContentType("text/css");

    File path = new File(servletContext.getRealPath("/WEB-INF/includes/css/"));

    File[] files = path.listFiles(...);

    for (File file : files) {
        InputStream is = new FileInputStream(file);
        IOUtils.copy(is, response.getOutputStream());
        IOUtils.closeQuietly(is);
    }

    response.flushBuffer();

    return new ResponseEntity<Void>(HttpStatus.OK);
}
8
  • See stackoverflow.com/questions/8226863/… But I note that your controller is producing text/css. The precise version of IE might be important. Commented Feb 6, 2014 at 13:42
  • Thanks for your answer. I'm actually using IE11. Commented Feb 6, 2014 at 13:44
  • 1
    What message converters are you using (what do you have in your <mvc:message-converters> section in your bean configuration file)? It might be a content-negotiation problem, with the result being served with the wrong MIME type. Commented Feb 6, 2014 at 13:47
  • Can you check headers of generated response. It should contains: Content-Type: text/css Commented Feb 6, 2014 at 13:52
  • 1
    I don't want to return String... Streams are better for memory usage. Commented Feb 6, 2014 at 14:00

2 Answers 2

6

I suspect the problem is due to your usage of HttpServletResponse.flushBuffer().

As the API of HttpServletRequest states:

Forces any content in the buffer to be written to the client. A call to this method automatically commits the response, meaning the status code and headers will be written.

My assumption would be that Spring attempts to set the Content-Type header on the HttpServletResponse after the method on your controller has returned. However, because you have committed the response with your call to HttpServletResponse.flushBuffer(), it cannot do this.

I would try either:

  • Injecting the HttpServletResponse into your controller and setting the header yourself in code before calling HttpServletResponse.flushBuffer()
  • Removing your usage of HttpServletRequest.flushBuffer()
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your answer! I already tried to do response.setContentType("text/css"); or setHeader(...) and even with content length but it's not working :(. Removing flushBuffer did not work either.
Actually, I added the setContentType before any write and it worked ! Thanks for it!
This solved my related problem--although I wasn't using flushBuffer(), I was using getInputStream() and IOUtils.copy(inputStream, response.getOutputStream()). Something about the way these methods operate was preventing response.setContentType from occurring. Moving response.setContentType above those lines of code solved my issue. Thanks!
6

Since you're writing the content directly to the output stream, you don't need to use @ResponseBody. You just need to ensure that you set the Content-Type response header. Also, it'd be better to return a ResponseEntity (rather than void) to indicate to Spring that you're handling the response yourself.

@RequestMapping(value = "/stylesheet.css", method = RequestMethod.GET)
public ResponseEntity css(HttpServletResponse response) {

    // Set the content-type
    response.setHeader("Content-Type", "text/css");

    File path = new File(servletContext.getRealPath("/WEB-INF/includes/css/"));

    File[] files = path.listFiles(...);

    for (File file : files) {
        InputStream is = new FileInputStream(file);
        IOUtils.copy(is, response.getOutputStream());
        IOUtils.closeQuietly(is);
    }

    response.flushBuffer();

    return new ResponseEntity(HttpStatus.OK)
}

1 Comment

First, thanks for your answer! Rob Blake got the answer a little before so I have accepted his answer, sorry. BUT, thanks for your helpful tips! I used @ResponseBody to test only (it was not there when I wrote the first method). I have understood the issue : we need to specify Content-Type before any write/flush to be handled correctly. Also, thanks for the closeQuietly and ResponseEntity tips.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.