From f8425a5f7b6bb8008acaf012d4b6c036117ef916 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Thu, 15 Jul 2010 08:49:18 +0000 Subject: [PATCH] SPR-7346 - @RequestHeader negation expressions (e.g. !Accept=text/plain) are not applied --- .../ServletAnnotationMappingUtils.java | 24 ++++++++----- .../ServletAnnotationControllerTests.java | 34 ++++++++++++++++++- .../web/bind/annotation/RequestMapping.java | 6 ++-- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationMappingUtils.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationMappingUtils.java index 067dc76fe4a..14d854a31fa 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationMappingUtils.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationMappingUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import org.springframework.web.util.WebUtils; * Helper class for annotation-based request mapping. * * @author Juergen Hoeller + * @author Arjen Poutsma * @since 2.5.2 */ abstract class ServletAnnotationMappingUtils { @@ -52,7 +53,8 @@ abstract class ServletAnnotationMappingUtils { /** * Check whether the given request matches the specified parameter conditions. - * @param params the parameter conditions, following {@link RequestMapping#params()} + * @param params the parameter conditions, following + * {@link org.springframework.web.bind.annotation.RequestMapping#params() RequestMapping.#params()} * @param request the current HTTP request to check */ public static boolean checkParameters(String[] params, HttpServletRequest request) { @@ -70,10 +72,11 @@ abstract class ServletAnnotationMappingUtils { } } else { - String key = param.substring(0, separator); + boolean negated = separator > 0 && param.charAt(separator - 1) == '!'; + String key = !negated ? param.substring(0, separator) : param.substring(0, separator - 1); String value = param.substring(separator + 1); if (!value.equals(request.getParameter(key))) { - return false; + return negated; } } } @@ -83,7 +86,8 @@ abstract class ServletAnnotationMappingUtils { /** * Check whether the given request matches the specified header conditions. - * @param headers the header conditions, following {@link RequestMapping#headers()} + * @param headers the header conditions, following + * {@link org.springframework.web.bind.annotation.RequestMapping#headers() RequestMapping.headers()} * @param request the current HTTP request to check */ public static boolean checkHeaders(String[] headers, HttpServletRequest request) { @@ -101,7 +105,8 @@ abstract class ServletAnnotationMappingUtils { } } else { - String key = header.substring(0, separator); + boolean negated = separator > 0 && header.charAt(separator - 1) == '!'; + String key = !negated ? header.substring(0, separator) : header.substring(0, separator - 1); String value = header.substring(separator + 1); if (isMediaTypeHeader(key)) { List requestMediaTypes = MediaType.parseMediaTypes(request.getHeader(key)); @@ -109,7 +114,8 @@ abstract class ServletAnnotationMappingUtils { boolean found = false; for (Iterator valIter = valueMediaTypes.iterator(); valIter.hasNext() && !found;) { MediaType valueMediaType = valIter.next(); - for (Iterator reqIter = requestMediaTypes.iterator(); reqIter.hasNext() && !found;) { + for (Iterator reqIter = requestMediaTypes.iterator(); + reqIter.hasNext() && !found;) { MediaType requestMediaType = reqIter.next(); if (valueMediaType.includes(requestMediaType)) { found = true; @@ -118,11 +124,11 @@ abstract class ServletAnnotationMappingUtils { } if (!found) { - return false; + return negated; } } else if (!value.equals(request.getHeader(key))) { - return false; + return negated; } } } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java index 734bd085004..e3a902eef2b 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java @@ -1315,7 +1315,24 @@ public class ServletAnnotationControllerTests { servlet.service(request, response); assertEquals("text", response.getContentAsString()); } - + + @Test + public void negatedContentTypeHeaders() throws ServletException, IOException { + initServlet(NegatedContentTypeHeadersController.class); + + MockHttpServletRequest request = new MockHttpServletRequest("POST", "/something"); + request.addHeader("Content-Type", "application/pdf"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("pdf", response.getContentAsString()); + + request = new MockHttpServletRequest("POST", "/something"); + request.addHeader("Content-Type", "text/html"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("non-pdf", response.getContentAsString()); + } + @Test public void acceptHeaders() throws ServletException, IOException { initServlet(AcceptHeadersController.class); @@ -2431,6 +2448,21 @@ public class ServletAnnotationControllerTests { } } + @Controller + public static class NegatedContentTypeHeadersController { + + @RequestMapping(value = "/something", headers = "content-type=application/pdf") + public void handlePdf(Writer writer) throws IOException { + writer.write("pdf"); + } + + @RequestMapping(value = "/something", headers = "content-type!=application/pdf") + public void handleNonPdf(Writer writer) throws IOException { + writer.write("non-pdf"); + } + + } + @Controller public static class AcceptHeadersController { diff --git a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java index da5ec5f43eb..3c376dd7c32 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java +++ b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java @@ -252,7 +252,8 @@ public @interface RequestMapping { * The parameters of the mapped request, narrowing the primary mapping. *

Same format for any environment: a sequence of "myParam=myValue" style * expressions, with a request only mapped if each such parameter is found - * to have the given value. "myParam" style expressions are also supported, + * to have the given value. Expressions can be negated by using the "!=" operator, + * as in "myParam!=myValue". "myParam" style expressions are also supported, * with such parameters having to be present in the request (allowed to have * any value). Finally, "!myParam" style expressions indicate that the * specified parameter is not supposed to be present in the request. @@ -275,7 +276,8 @@ public @interface RequestMapping { * The headers of the mapped request, narrowing the primary mapping. *

Same format for any environment: a sequence of "My-Header=myValue" style * expressions, with a request only mapped if each such header is found - * to have the given value. "My-Header" style expressions are also supported, + * to have the given value. Expressions can be negated by using the "!=" operator, + * as in "My-Header!=myValue". "My-Header" style expressions are also supported, * with such headers having to be present in the request (allowed to have * any value). Finally, "!My-Header" style expressions indicate that the * specified header is not supposed to be present in the request.