Announcement Announcement Module
Collapse
No announcement yet.
Building REST service for returning images to browser Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Building REST service for returning images to browser

    Hi, I'm having difficulty implementing a small REST controller for serving images. Spring 3 has the BufferedImageHttpMessageConverter which would seem to fit the bill perfectly and make the controller extremely simple to implement (bar registering said converter):

    Code:
    @Controller
    public class RestScreenshotController {
        @RequestMapping("/screenshot/key/{keyId}")
        @ResponseBody
        public BufferedImage getKeyScreenshot(@PathVariable String keyId, OutputStream outputStream) {
            try {
                // TODO: this just returns a hardcoded image
                return ImageIO.read(new File("src/main/webapp/images/logo.png"));
            } catch(IOException e) {
                System.out.println(e.getMessage());
            }
        }
    }
    The problem is, this is supposed to be used by a GWT application with some image widget configured with the appropriate URL. This means that it is the browser making the request.
    In the case of Firefox, content-negotiation breaks down because Firefox does not include any mime-types for images in it's Accept HTTP header.
    It seems to me that content-negotiation is too limited, that we need some hooks into the process, or maybe we just need to be able to specify a list of mime-types in the @ResponseBody, that will appropriately affect the negotiation process?
    I know that in JSR-311 you have a @Produces{mime-types...} annotation for a similar purpose.

  • #2
    Ahaa...

    Arh, I just noticed that Firefox changes it's accept header when requesting images with img tags to: Accept: image/png,image/*;q=0.8,*/*;q=0.5

    My problem was the default registered AnnotationMethodHandlerAdapter was tried before the one I configured explicitly with the bufimgconverter.
    Since I have Jackson in the classpath for JSON support in another service, it was gobbling the request before the bufimgconverter could get it's hands on it (and producing strange errors trying to serialize the image to JSON ).

    Now I have the following in my DispatcherServlet configuration:
    Code:
        <context:component-scan base-package="com.brunata.weblang.rest"/>
        <mvc:annotation-driven/>
     
        <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
            <property name="order" value="1"/>
            <property name="messageConverters">
                <list>
                    <bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/>
                </list>
            </property>
        </bean>
    This means this handler adapter is tried before the one Spring MVC inserts automatically.

    Comment


    • #3
      Gah!

      I was ahead of myself. In case someone else reads this, I feel obliged to correct my previous post.

      The problem is that only one HandlerAdapter is used to handle a request. Now that we have moved our own AnnotationMethodHandlerAdapter before the Spring auto-configured adapter, we need to register all the default converters manually, or other REST services will stop working:
      Code:
          <!--
              Configure a handler adapter with a
              BufferedImageHttpMessageConverter. This must be done manually
              due to http://jira.springframework.org/browse/SPR-5975
          -->
          <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
              <!--
                  NOTE: it is important to specify the order property, so this
                  adapter will be attempted before the HandlerAdapter that
                  Spring MVC automatically configures. This is because a
                  MappingJacksonHttpMessageConverter is registered
                  automatically with the default adapter that will attempt to
                  handle any Java object including BufferedImage.
              -->
              <property name="order" value="1" />
              <property name="messageConverters">
                  <list>
                      <!-- Default converters -->
                      <bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
                      <bean class="org.springframework.http.converter.FormHttpMessageConverter"/>
                      <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
                      <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
                      
                      <!-- Converter for images -->
                      <bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/>
                      
                      <!-- This must come after our image converter -->
                      <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
                  </list>
              </property>
          </bean>
      It might be worthwhile to add some of this information to the Spring documentation which is very sparse on this subject.

      Comment


      • #4
        Also...

        Also, why on earth isn't java.awt.BufferedImage recognized as a common type with an associated registered converter? This is handled all automatically in JSR-311/JAX-RS, no need to mangle a ton of XML in the right order.

        Comment


        • #5
          Thanks for the tips. However I could still not get it working. I am getting "org.springframework.web.HttpMediaTypeNotAcceptabl eException: Could not find acceptable representation" error.

          AnnotationMethodHandlerAdapter is configured as
          <bean class="org.springframework.web.servlet.mvc.annotat ion.AnnotationMethodHandlerAdapter">
          <property name="order" value="1" />
          <property name="messageConverters">
          <util:list id="beanList">
          <!-- Default converters -->
          <bean class="org.springframework.http.converter.StringHt tpMessageConverter"/>

          <!-- Converter for images -->
          <bean class="org.springframework.http.converter.Buffered ImageHttpMessageConverter"/>
          </util:list>
          </property>
          </bean>

          Comment


          • #6
            It worked in Google Chrome but not in Firefox and IE8.

            Comment


            • #7
              Hmm..

              Try checking the Accept header your browser sends with something like the Firefox plugin Live Http headers.

              But actually I wouldn't really recommend my approach. We later discovered that when you right-click on the image in Firefox and select "View picture" from the context menu, Firefox reverts to the Accept header without image types, causing context negotiation to break down.

              Instead I would either:
              1) Avoid content negotiation and just send the image in a specific format always.
              2) Use a full JAX-RS implementation.

              Comment

              Working...
              X