开发者

Spring MVC Content Negotiation with HttpMessageConverter

For a recent project we wanted to support XML and another format for our resopnses.

However, we could not control the Accept header. Hence we configured a ContentNegotiatingViewResolver to use a request parameter instead:

<bean id="viewResolver" clas开发者_如何转开发s="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="favorParameter" value="true" />
    <property name="parameterName" value="format" />
    <property name="ignoreAcceptHeader" value="true" />
    <property name="defaultContentType" value="application/xml" />
    <property name="mediaTypes">
        <map>
            <entry key="xml" value="application/xml" />
            <entry key="foo" value="application/x-foo" />
       </map>
    </property>
    <property name="viewResolvers">
        <list>
            <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver" >
                <property name="basename" value="views-xml" />
            </bean>
            <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver" >
                <property name="basename" value="views-foo" />
            </bean>
        </list>
    </property>
</bean>

Now however, I was wondering if I could move to using @ResponseBody and HttpMessageConverter implementations to simplify the amount of code I need to maintain.

However, is there a similar way to ensure that a reqeust parameter is used for content negotiation, instead of the Accept header?


There is a workaround, as described at https://jira.springframework.org/browse/SPR-7517

Create a subclass of AnnotationMethodHandlerAdapter:

package org.nkl.spring;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;

public class MyAnnotationMethodHandlerAdapter extends AnnotationMethodHandlerAdapter {

   private Logger logger = LoggerFactory.getLogger(MyAnnotationMethodHandlerAdapter.class);
   private String requestParam = "accept";
   private Map<String, MediaType> mediaTypesMap;

   @Override protected HttpInputMessage createHttpInputMessage(HttpServletRequest request) throws Exception {
      HttpInputMessage message = super.createHttpInputMessage(request);
      String accept = request.getParameter(requestParam);
      if (accept == null || accept.isEmpty()) {
         logger.info(String.format("Request parameter [%s] not found. Using standard HttpInputMessage", requestParam));
         return message;
      } else {
         logger.info(String.format("Request parameter [%s] was [%s]", requestParam, accept));
         MediaType mediaType = mediaTypesMap.get(accept);
         if (mediaType == null) {
            logger.info(String.format("Suitable MediaType not found. Using standard HttpInputMessage"));
            return message;
         } else {
            logger.info(String.format("Suitable MediaType [%s] found. Using custom HttpInputMessage", mediaType));
            return new MyHttpInputMessage(message, mediaTypesMap.get(accept));
         }
      }
   }

   public void setMediaTypesMap(Map<String, MediaType> mediaTypesMap) {
      this.mediaTypesMap = mediaTypesMap;
   }

   public void setRequestParam(String requestParam) {
      this.requestParam = requestParam;
   }
}

Create a Decorator of HttpInputMessage:

package org.nkl.spring;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.MediaType;

public class MyHttpInputMessage implements HttpInputMessage {

   private HttpInputMessage delegate;

   public MyHttpInputMessage(HttpInputMessage delagate, MediaType mediaType) {
      this.delegate = delagate;
      this.delegate.getHeaders().setAccept(Arrays.asList(mediaType));
   }

   @Override public InputStream getBody() throws IOException {
      return this.delegate.getBody();
   }

   @Override public HttpHeaders getHeaders() {
      return this.delegate.getHeaders();
   }
}

Configure your bean like:

   <bean class="org.nkl.spring.MyAnnotationMethodHandlerAdapter">
      <property name="requestParam" value="format" />
      <property name="mediaTypesMap">
         <util:map>
            <entry key="plain" value="text/plain" />
            <entry key="xml" value="text/xml" />
         </util:map>
      </property>
      <property name="messageConverters">
         <util:list>
            ... converters go here ...
         </util:list>
      </property>
   </bean>


Newer versions of Spring can now do this. The ContentNegotiationManagerFactoryBean can create a content negotiation manager that does precisely what you want here. See my answer to a related question.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜