title: Request mapping using headers in Spring mvc
body: <p>Currently I am using Spring mvc request mapping using headers.</p>
  
  <p>Following are the two methods I have:</p>
  
  <p>Method 1: </p>
  
  <pre><code>@RequestMapping(value = "/accounts", method = RequestMethod.GET,
  headers="Accept=application/vnd.se.company1.product1+json; version=2.0")
  
  @ResponseBody
  public ResponseEntity&lt;String&gt; loginVerionOne(final HttpServletRequest request){}
  </code></pre>
  
  <p>Method 2: </p>
  
  <pre><code>@RequestMapping(value = "/accounts", method = RequestMethod.GET,
  headers="Accept=application/vnd.se.company1.product1+json; version=3.0")
  
  @ResponseBody
  public ResponseEntity&lt;String&gt; loginVersionTwo(final HttpServletRequest request) {}
  </code></pre>
  
  <p>The problem here is that when client sends accept header in it's request in below format:
  <code>Accept=application/vnd.se.company1.product1+json; version=3.0</code></p>
  
  <p>Then spring maps to the first method only even if the version is 3.0.<br/>
  I have read in one of the post that spring ignores request mapping after <kbd>;</kbd> (semi-colon) in request mapping using headers.</p>
  
  <p>We are using custom accept header where we need to keep content type and version separately.</p>
  
  <p>The mapping works if I use Accept header in below format:
  <code>Accept=application/vnd.se.company1.product1.v2.0+json</code></p>
  
  <p>But I do not want to use above format.<br/>
  Any help in this issue will be appreciated.</p>
  
  <p>Following is my code used :</p>
  
  <pre><code>RequestMappingController
  ========================
  @RequestMapping(value = "/helloworld",method=RequestMethod.GET)
          @SubdomainMapping(subdomains="application/vnd.se.company1.product1+json;version=2.0")
      public ModelAndView helloWordNew(HttpServletRequest req){
           String message = "Hello World Version 2.0";
           return new ModelAndView("helloworld", "message",message);
      }
      @RequestMapping(value = "/helloworld",method=RequestMethod.GET)
      @SubdomainMapping(subdomains="application/vnd.se.company1.product1+json;version=3.0")
      public ModelAndView helloWord(HttpServletRequest req){
            String message = "Hello World Version 3.0";
            return new ModelAndView("helloworld", "message",message);
      }
  
  RequestCondition:
  ================
  
  public class SubdomainRequestCondition implements
          RequestCondition&lt;SubdomainRequestCondition&gt; {
  
      private final Set&lt;String&gt; subdomains;
  
  
      public SubdomainRequestCondition(String subdomains) {
          this(Arrays.asList(subdomains));
      }
  
      public SubdomainRequestCondition(Collection&lt;String&gt; subdomains) {
          this.subdomains = Collections.unmodifiableSet(new HashSet&lt;String&gt;(
                  subdomains));
      }
  
      @Override
      public SubdomainRequestCondition combine(SubdomainRequestCondition other) {
          Set&lt;String&gt; allRoles = new LinkedHashSet&lt;String&gt;(this.subdomains);
          allRoles.addAll(other.subdomains);
          return new SubdomainRequestCondition(allRoles);
      }
  
      @Override
      public SubdomainRequestCondition getMatchingCondition(
              HttpServletRequest request) {
              String header = request.getHeader("Accept");
              for (String s : subdomains) {
                  if (s.equals(header)) {
                      return this;
                  }
              }
          return null;
      }
  
     @Override
      public int compareTo(SubdomainRequestCondition other,
              HttpServletRequest request) {
          return org.apache.commons.collections.CollectionUtils.removeAll(other.subdomains, this.subdomains).size();
      }
  }
  
  HandlerMapping
  ==============
  
  public class CustomRequestMappingHandlerMapping extends
          RequestMappingHandlerMapping {
     @Override
      protected RequestCondition&lt;?&gt; getCustomTypeCondition(Class&lt;?&gt; handlerType) {
          SubdomainMapping typeAnnotation = AnnotationUtils.findAnnotation(
                  handlerType, SubdomainMapping.class);
          return createCondition(typeAnnotation);
      }
  
      @Override
      protected RequestCondition&lt;?&gt; getCustomMethodCondition(Method method) {
          SubdomainMapping methodAnnotation = AnnotationUtils.findAnnotation(
                  method, SubdomainMapping.class);
          return createCondition(methodAnnotation);
      }
  
      private RequestCondition&lt;?&gt; createCondition(SubdomainMapping accessMapping) {
          return (accessMapping != null) ? new SubdomainRequestCondition(accessMapping.subdomains()) : null;
      }
  }
  
  
  
     applicationContext.xml
      ======================
      &lt;context:component-scan base-package="com.net.controllers"/&gt;
  
          &lt;bean id="viewResolver"
              class="org.springframework.web.servlet.view.InternalResourceViewResolver"&gt;
  
              &lt;property name="prefix"&gt;
                  &lt;value&gt;/WEB-INF/views/&lt;/value&gt;
              &lt;/property&gt;
              &lt;property name="suffix"&gt;
                  &lt;value&gt;.jsp&lt;/value&gt;
              &lt;/property&gt;
          &lt;/bean&gt;
  
          &lt;bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"&gt;
              &lt;property name="webBindingInitializer"&gt;
              &lt;bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"&gt;
                 &lt;property name="validator"&gt;
                      &lt;bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/&gt;
                  &lt;/property&gt;
              &lt;/bean&gt;
          &lt;/property&gt;
          &lt;property name="messageConverters"&gt;
              &lt;list&gt;
                  &lt;bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"&gt;&lt;/bean&gt;
                  &lt;bean class="org.springframework.http.converter.StringHttpMessageConverter"&gt;&lt;/bean&gt;
                  &lt;bean class="org.springframework.http.converter.ResourceHttpMessageConverter"&gt;&lt;/bean&gt;
                  &lt;bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"&gt;&lt;/bean&gt;
                  &lt;bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"&gt;&lt;/bean&gt;
                  &lt;bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"&gt;&lt;/bean&gt;
                  &lt;bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"&gt;&lt;/bean&gt;
              &lt;/list&gt;
          &lt;/property&gt;
          &lt;/bean&gt;
  &lt;bean id="re" class="com.net.controllers.WebConfig"/&gt;
  &lt;/beans&gt;
  
  WebConfig.java
  ==============
  
  @Configuration
  public class WebConfig extends WebMvcConfigurationSupport {
  
      @Autowired
  
      @Override
      @Bean
      public RequestMappingHandlerMapping requestMappingHandlerMapping() {
          CustomRequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping();
          handlerMapping.setOrder(0);
          handlerMapping.setInterceptors(getInterceptors());
          return handlerMapping;
      }
  
       @Bean
          @Override
          public FormattingConversionService mvcConversionService() {
  
              // Use the DefaultFormattingConversionService but do not register defaults
              DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false);
              return conversionService;
          }
  }
  </code></pre>