Results 1 to 9 of 9

Thread: Wrong Content-Type on response.

  1. #1

    Default Wrong Content-Type on response.

    Hi,

    I have a controller method (3.0) that is returning a byte array for the purposes of downloading a file attachment:

    public @ResponseBody byte[] viewAttachment(....);

    I have tried to setContentType and also setHeader but these are ignored/overwritten in the final written output.

    The result of the AnnotationMethodHandlerAdapter is that the Content Type is set to one of the values passed in the Accept header in the request.

    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

    Results in the Content-Type being returned as text/html which confuses the browser to the content type. I.e it wont allow the browser to open a PDF file directly.

    You might ask why I am not using a ResponseEntity<byte[]> - There is a reason for that: If I use the ResponseEntity I can not override the no-cache and no-store headers. These can only be modified on a per-application basis which is undesirable.

    Many Thanks

    Chris

  2. #2
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,632

    Default

    Why not simply stream the array yourself, that way you can set the content type and all you want yourself.

    Also why wouldn't you be able to override the headers when return the ResponseEntity you can simply set them on the response or pass them in as headers to the ResponseEntity...

    Another solution could be to extend the ByteArrayHttpMessageConverter to correctly determine the content type (this is also suggested in the reference guide).
    Last edited by Marten Deinum; May 16th, 2012 at 09:09 AM.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  3. #3

    Default

    Quote Originally Posted by Marten Deinum View Post
    Why not simply stream the array yourself, that way you can set the content type and all you want yourself.

    Also why wouldn't you be able to override the headers when return the ResponseEntity you can simply set them on the response or pass them in as headers to the ResponseEntity...

    Another solution could be to extend the ByteArrayHttpMessageConverter to correctly determine the content type (this is also suggested in the reference guide).
    I researched this about the headers. This is in WebContentGenerator.preventCaching. You can only configure on the application to either use the header or don't. There is no mixing. I need to use this caching for the rest of the application. I would need to do this at the Servlet layer, and Spring advocates not doing this, plus it adds a complexity I do not like.

    I will look into streaming the array myself though.

    Thanks

    Chris

  4. #4
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,632

    Default

    The headers are set first before the method is invoked, so in your handler method you should be able to override/reset those values so that no-caching is done.

    Edit: Forget the interceptor that will probably only interfere ...
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  5. #5

    Default

    I have tried this already. There is no way to override those headers. When they are set in my controller method they do not end up in the final output.

  6. #6
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,632

    Default

    Hmm.. It works for me when doing direct writing to the response, set the header (for no caching etc) then simply write the to the output stream or writer of the response. Works like a charm.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  7. #7

    Default

    I have not tried direct response writing yet. I will try that later today. Thanks.

    For the moment I am trying to figure out why it is not working out-the-box as I would expect it to:

    AnnotationMethodHandlerAdapter breaks down the "Accept" header into

    "text/html"
    "application/xhtml+xml"
    "application/xml;q=0.9"
    "*/*;q=0.8"


    What then happens is it fetches all the available message converters and attempts to discover which message converter is able write the desired output (byte[]) with each of the above "Accept" values.

    ByteArrayHttpMessageConverter has the following "supportedMediaTypes"

    "application/octet-stream"
    "*/*"

    So a call to ByteArrayHttpMessageConverter.canWrite(byte[], "text/html", ...) will evaluate to true as internally "text/html" will match with "*/*

    When ByteArrayHttpMessageConverter.write is called:

    if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) {
    contentType = getDefaultContentType(t);
    }

    if (contentType != null) {
    headers.setContentType(contentType);
    }
    The content type will automatically be set to "text/html" in the second if block, and this is what will be returned to the client.

    If I hack a minor change into the process flow through the debugger and remove "*/* from the "supportedMediaTypes" this will result in "text/html" failing to match in the ByteArrayHttpMessageConverter. The next two will also fail to match ending up at the last item "*/* being called:

    ByteArrayHttpMessageConverter.canWrite(byte[], "*/*", ...)

    Within the above code block the content type will match as isWildcardType so the content type will be set as the default content type "application/octet-stream" which works perfect for my purposes.

    I do not understand fully the reasoning for this strange matching of Accept types but if I can get the ByteArrayHttpMessageConverter to NOT have the "*/*" supported media type then this will acceptable for me.

    Thanks

    Chris

  8. #8

    Default

    I have fixed this by declaring:

    <bean class = "org.springframework.web.servlet.mvc.annotation.An notationMethodHandlerAdapter">
    <property name="messageConverters">
    <array>
    <bean class = "org.springframework.http.converter.ByteArrayHttpM essageConverter">
    <property name="supportedMediaTypes">
    <list>
    <value>application/octet-stream</value>
    </list>
    </property>
    </bean>
    </array>
    </property>
    </bean>
    However, this needs to be read in before:

    <mvc:annotation-driven/>
    Can anyone explain the reasoning behind the code?

  9. #9

    Default

    When I define the converter above the rest of the converters are ignored.

    Can I use that config above but also say use the defaults as well?

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •