From edc80ffa956214922ff4892250032a689676f720 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Thu, 9 Feb 2012 11:20:24 +0100 Subject: [PATCH] Use request contentType/encoding in ServletServetHttpRequest/Response ServletServerHttpRequest now falls back on the contentType and the the characterEncoding of the ServletRequest, if the headers of the incoming request don't specify one. Similary ServletServerHttpResponse sets the contentType and the characterEncoding of the ServletResponse if not already set. This allows using the CharacterEncodingFilter to set a character encoding where the request doesn't specify one and have it be used in HttpMessageConverter's. SPR-9096 --- .../resources/changelog.txt | 1 + .../org/springframework/http/MediaType.java | 10 ++++++- .../http/server/ServletServerHttpRequest.java | 27 ++++++++++++++++--- .../server/ServletServerHttpResponse.java | 10 ++++++- .../server/ServletServerHttpRequestTests.java | 10 +++++-- .../ServletServerHttpResponseTests.java | 15 ++++++++--- 6 files changed, 62 insertions(+), 11 deletions(-) diff --git a/build-spring-framework/resources/changelog.txt b/build-spring-framework/resources/changelog.txt index 6314a068d0..5e6ef950c6 100644 --- a/build-spring-framework/resources/changelog.txt +++ b/build-spring-framework/resources/changelog.txt @@ -42,6 +42,7 @@ Changes in version 3.1.1 (2012-02-13) * fixed request mapping bug involving direct vs pattern path matches with HTTP methods * removed check for HTTP "POST" when resolving multipart request controller method arguments * updated @RequestMapping and reference docs wrt differences between @MVC 3.1 and @MVC 2.5-3.0 +* ServletServerHttpRequest/Response fall back on the Content-Type and encoding of the request Changes in version 3.1 GA (2011-12-12) diff --git a/org.springframework.web/src/main/java/org/springframework/http/MediaType.java b/org.springframework.web/src/main/java/org/springframework/http/MediaType.java index d467e1feae..6d0f75fe4f 100644 --- a/org.springframework.web/src/main/java/org/springframework/http/MediaType.java +++ b/org.springframework.web/src/main/java/org/springframework/http/MediaType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -450,6 +450,14 @@ public class MediaType implements Comparable { return this.parameters.get(name); } + /** + * Return all generic parameter values. + * @return a read-only map, possibly empty, never null + */ + public Map getParameters() { + return parameters; + } + /** * Indicate whether this {@code MediaType} includes the given media type. *

For instance, {@code text/*} includes {@code text/plain} and {@code text/html}, and {@code application/*+xml} diff --git a/org.springframework.web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java b/org.springframework.web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java index e93367b651..3c5459e615 100644 --- a/org.springframework.web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java +++ b/org.springframework.web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -25,15 +25,19 @@ import java.io.Writer; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; +import java.nio.charset.Charset; import java.util.Arrays; import java.util.Enumeration; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; + import javax.servlet.http.HttpServletRequest; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; import org.springframework.util.Assert; /** @@ -90,14 +94,31 @@ public class ServletServerHttpRequest implements ServerHttpRequest { public HttpHeaders getHeaders() { if (this.headers == null) { this.headers = new HttpHeaders(); - for (Enumeration headerNames = this.servletRequest.getHeaderNames(); headerNames.hasMoreElements();) { + for (Enumeration headerNames = this.servletRequest.getHeaderNames(); headerNames.hasMoreElements();) { String headerName = (String) headerNames.nextElement(); - for (Enumeration headerValues = this.servletRequest.getHeaders(headerName); + for (Enumeration headerValues = this.servletRequest.getHeaders(headerName); headerValues.hasMoreElements();) { String headerValue = (String) headerValues.nextElement(); this.headers.add(headerName, headerValue); } } + // HttpServletRequest exposes some headers as properties: we should include those if not already present + if (this.headers.getContentType() == null && this.servletRequest.getContentType() != null) { + MediaType contentType = MediaType.parseMediaType(this.servletRequest.getContentType()); + this.headers.setContentType(contentType); + } + if (this.headers.getContentType() != null && this.headers.getContentType().getCharSet() == null && + this.servletRequest.getCharacterEncoding() != null) { + MediaType oldContentType = this.headers.getContentType(); + Charset charSet = Charset.forName(this.servletRequest.getCharacterEncoding()); + Map params = new HashMap(oldContentType.getParameters()); + params.put("charset", charSet.toString()); + MediaType newContentType = new MediaType(oldContentType.getType(), oldContentType.getSubtype(), params); + this.headers.setContentType(newContentType); + } + if (this.headers.getContentLength() == -1 && this.servletRequest.getContentLength() != -1) { + this.headers.setContentLength(this.servletRequest.getContentLength()); + } } return this.headers; } diff --git a/org.springframework.web/src/main/java/org/springframework/http/server/ServletServerHttpResponse.java b/org.springframework.web/src/main/java/org/springframework/http/server/ServletServerHttpResponse.java index 9752c4259d..985085e51e 100644 --- a/org.springframework.web/src/main/java/org/springframework/http/server/ServletServerHttpResponse.java +++ b/org.springframework.web/src/main/java/org/springframework/http/server/ServletServerHttpResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -83,6 +83,14 @@ public class ServletServerHttpResponse implements ServerHttpResponse { this.servletResponse.addHeader(headerName, headerValue); } } + // HttpServletResponse exposes some headers as properties: we should include those if not already present + if (this.servletResponse.getContentType() == null && this.headers.getContentType() != null) { + this.servletResponse.setContentType(this.headers.getContentType().toString()); + } + if (this.servletResponse.getCharacterEncoding() == null && this.headers.getContentType() != null && + this.headers.getContentType().getCharSet() != null) { + this.servletResponse.setCharacterEncoding(this.headers.getContentType().getCharSet().name()); + } this.headersWritten = true; } } diff --git a/org.springframework.web/src/test/java/org/springframework/http/server/ServletServerHttpRequestTests.java b/org.springframework.web/src/test/java/org/springframework/http/server/ServletServerHttpRequestTests.java index 30fd57b6b7..1a2ec75e57 100644 --- a/org.springframework.web/src/test/java/org/springframework/http/server/ServletServerHttpRequestTests.java +++ b/org.springframework.web/src/test/java/org/springframework/http/server/ServletServerHttpRequestTests.java @@ -1,11 +1,11 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -17,6 +17,7 @@ package org.springframework.http.server; import java.net.URI; +import java.nio.charset.Charset; import java.util.List; import org.junit.Before; @@ -24,6 +25,7 @@ import org.junit.Test; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.util.FileCopyUtils; @@ -67,6 +69,8 @@ public class ServletServerHttpRequestTests { mockRequest.addHeader(headerName, headerValue1); String headerValue2 = "value2"; mockRequest.addHeader(headerName, headerValue2); + mockRequest.setContentType("text/plain"); + mockRequest.setCharacterEncoding("UTF-8"); HttpHeaders headers = request.getHeaders(); assertNotNull("No HttpHeaders returned", headers); @@ -75,6 +79,8 @@ public class ServletServerHttpRequestTests { assertEquals("Invalid header values returned", 2, headerValues.size()); assertTrue("Invalid header values returned", headerValues.contains(headerValue1)); assertTrue("Invalid header values returned", headerValues.contains(headerValue2)); + assertEquals("Invalid Content-Type", new MediaType("text", "plain", Charset.forName("UTF-8")), + headers.getContentType()); } @Test diff --git a/org.springframework.web/src/test/java/org/springframework/http/server/ServletServerHttpResponseTests.java b/org.springframework.web/src/test/java/org/springframework/http/server/ServletServerHttpResponseTests.java index bad9ccdb84..fd9392f108 100644 --- a/org.springframework.web/src/test/java/org/springframework/http/server/ServletServerHttpResponseTests.java +++ b/org.springframework.web/src/test/java/org/springframework/http/server/ServletServerHttpResponseTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2012 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. @@ -16,16 +16,19 @@ package org.springframework.http.server; +import java.nio.charset.Charset; import java.util.List; -import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.util.FileCopyUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.util.FileCopyUtils; + +import static org.junit.Assert.*; /** * @author Arjen Poutsma @@ -56,12 +59,16 @@ public class ServletServerHttpResponseTests { headers.add(headerName, headerValue1); String headerValue2 = "value2"; headers.add(headerName, headerValue2); + headers.setContentType(new MediaType("text", "plain", Charset.forName("UTF-8"))); response.close(); assertTrue("Header not set", mockResponse.getHeaderNames().contains(headerName)); List headerValues = mockResponse.getHeaders(headerName); assertTrue("Header not set", headerValues.contains(headerValue1)); assertTrue("Header not set", headerValues.contains(headerValue2)); + assertEquals("Invalid Content-Type", "text/plain;charset=UTF-8", mockResponse.getHeader("Content-Type")); + assertEquals("Invalid Content-Type", "text/plain;charset=UTF-8", mockResponse.getContentType()); + assertEquals("Invalid Content-Type", "UTF-8", mockResponse.getCharacterEncoding()); } @Test