first bunch of backports from 3.1 M2 to 3.0.6
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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,
|
||||
@@ -177,11 +177,15 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
||||
String[] tokens = value.split(",\\s*");
|
||||
for (String token : tokens) {
|
||||
int paramIdx = token.indexOf(';');
|
||||
String charsetName;
|
||||
if (paramIdx == -1) {
|
||||
result.add(Charset.forName(token));
|
||||
charsetName = token;
|
||||
}
|
||||
else {
|
||||
result.add(Charset.forName(token.substring(0, paramIdx)));
|
||||
charsetName = token.substring(0, paramIdx);
|
||||
}
|
||||
if (!charsetName.equals("*")) {
|
||||
result.add(Charset.forName(charsetName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -310,7 +314,11 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
||||
* @param eTag the new entity tag
|
||||
*/
|
||||
public void setETag(String eTag) {
|
||||
set(ETAG, quote(eTag));
|
||||
if (eTag != null) {
|
||||
Assert.isTrue(eTag.startsWith("\"") || eTag.startsWith("W/"), "Invalid eTag, does not start with W/ or \"");
|
||||
Assert.isTrue(eTag.endsWith("\""), "Invalid eTag, does not end with \"");
|
||||
}
|
||||
set(ETAG, eTag);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -318,7 +326,7 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
||||
* @return the entity tag
|
||||
*/
|
||||
public String getETag() {
|
||||
return unquote(getFirst(ETAG));
|
||||
return getFirst(ETAG);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,7 +370,7 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
||||
* @param ifNoneMatch the new value of the header
|
||||
*/
|
||||
public void setIfNoneMatch(String ifNoneMatch) {
|
||||
set(IF_NONE_MATCH, quote(ifNoneMatch));
|
||||
set(IF_NONE_MATCH, ifNoneMatch);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -373,7 +381,7 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (Iterator<String> iterator = ifNoneMatchList.iterator(); iterator.hasNext();) {
|
||||
String ifNoneMatch = iterator.next();
|
||||
builder.append(quote(ifNoneMatch));
|
||||
builder.append(ifNoneMatch);
|
||||
if (iterator.hasNext()) {
|
||||
builder.append(", ");
|
||||
}
|
||||
@@ -392,7 +400,7 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
||||
if (value != null) {
|
||||
String[] tokens = value.split(",\\s*");
|
||||
for (String token : tokens) {
|
||||
result.add(unquote(token));
|
||||
result.add(token);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -452,31 +460,6 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
||||
|
||||
// Utility methods
|
||||
|
||||
private String quote(String s) {
|
||||
Assert.notNull(s);
|
||||
if (!s.startsWith("\"")) {
|
||||
s = "\"" + s;
|
||||
}
|
||||
if (!s.endsWith("\"")) {
|
||||
s = s + "\"";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
private String unquote(String s) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
if (s.startsWith("\"")) {
|
||||
s = s.substring(1);
|
||||
}
|
||||
if (s.endsWith("\"")) {
|
||||
s = s.substring(0, s.length() - 1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
private long getFirstDate(String headerName) {
|
||||
String headerValue = getFirst(headerName);
|
||||
if (headerValue == null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -187,7 +187,7 @@ public class MediaType implements Comparable<MediaType> {
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link MediaType} for the given primary type.
|
||||
* Create a new {@code MediaType} for the given primary type.
|
||||
* <p>The {@linkplain #getSubtype() subtype} is set to <code>*</code>, parameters empty.
|
||||
* @param type the primary type
|
||||
* @throws IllegalArgumentException if any of the parameters contain illegal characters
|
||||
@@ -197,7 +197,7 @@ public class MediaType implements Comparable<MediaType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link MediaType} for the given primary type and subtype.
|
||||
* Create a new {@code MediaType} for the given primary type and subtype.
|
||||
* <p>The parameters are empty.
|
||||
* @param type the primary type
|
||||
* @param subtype the subtype
|
||||
@@ -208,18 +208,18 @@ public class MediaType implements Comparable<MediaType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link MediaType} for the given type, subtype, and character set.
|
||||
* Create a new {@code MediaType} for the given type, subtype, and character set.
|
||||
* @param type the primary type
|
||||
* @param subtype the subtype
|
||||
* @param charSet the character set
|
||||
* @throws IllegalArgumentException if any of the parameters contain illegal characters
|
||||
*/
|
||||
public MediaType(String type, String subtype, Charset charSet) {
|
||||
this(type, subtype, Collections.singletonMap(PARAM_CHARSET, charSet.toString()));
|
||||
this(type, subtype, Collections.singletonMap(PARAM_CHARSET, charSet.name()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link MediaType} for the given type, subtype, and quality value.
|
||||
* Create a new {@code MediaType} for the given type, subtype, and quality value.
|
||||
*
|
||||
* @param type the primary type
|
||||
* @param subtype the subtype
|
||||
@@ -231,7 +231,7 @@ public class MediaType implements Comparable<MediaType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy-constructor that copies the type and subtype of the given {@link MediaType},
|
||||
* Copy-constructor that copies the type and subtype of the given {@code MediaType},
|
||||
* and allows for different parameter.
|
||||
* @param other the other media type
|
||||
* @param parameters the parameters, may be <code>null</code>
|
||||
@@ -242,7 +242,7 @@ public class MediaType implements Comparable<MediaType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link MediaType} for the given type, subtype, and parameters.
|
||||
* Create a new {@code MediaType} for the given type, subtype, and parameters.
|
||||
* @param type the primary type
|
||||
* @param subtype the subtype
|
||||
* @param parameters the parameters, may be <code>null</code>
|
||||
@@ -322,7 +322,7 @@ public class MediaType implements Comparable<MediaType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether the {@linkplain #getType() type} is the wildcard character <code>*</code> or not.
|
||||
* Indicates whether the {@linkplain #getType() type} is the wildcard character <code>*</code> or not.
|
||||
*/
|
||||
public boolean isWildcardType() {
|
||||
return WILDCARD_TYPE.equals(type);
|
||||
@@ -336,13 +336,22 @@ public class MediaType implements Comparable<MediaType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether the {@linkplain #getSubtype() subtype} is the wildcard character <code>*</code> or not.
|
||||
* Indicates whether the {@linkplain #getSubtype() subtype} is the wildcard character <code>*</code> or not.
|
||||
* @return whether the subtype is <code>*</code>
|
||||
*/
|
||||
public boolean isWildcardSubtype() {
|
||||
return WILDCARD_TYPE.equals(subtype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this media type is concrete, i.e. whether neither the type or subtype is a wildcard
|
||||
* character <code>*</code>.
|
||||
* @return whether this media type is concrete
|
||||
*/
|
||||
public boolean isConcrete() {
|
||||
return !isWildcardType() && !isWildcardSubtype();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the character set, as indicated by a <code>charset</code> parameter, if any.
|
||||
* @return the character set; or <code>null</code> if not available
|
||||
@@ -372,9 +381,9 @@ public class MediaType implements Comparable<MediaType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether this {@link MediaType} includes the given media type.
|
||||
* <p>For instance, {@code text/*} includes {@code text/plain}, {@code text/html}, and {@code application/*+xml}
|
||||
* includes {@code application/soap+xml}, etc. This method is non-symmetic.
|
||||
* Indicate whether this {@code MediaType} includes the given media type.
|
||||
* <p>For instance, {@code text/*} includes {@code text/plain} and {@code text/html}, and {@code application/*+xml}
|
||||
* includes {@code application/soap+xml}, etc. This method is <b>not</b> symmetric.
|
||||
* @param other the reference media type with which to compare
|
||||
* @return <code>true</code> if this media type includes the given media type; <code>false</code> otherwise
|
||||
*/
|
||||
@@ -407,9 +416,9 @@ public class MediaType implements Comparable<MediaType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether this {@link MediaType} is compatible with the given media type.
|
||||
* Indicate whether this {@code MediaType} is compatible with the given media type.
|
||||
* <p>For instance, {@code text/*} is compatible with {@code text/plain}, {@code text/html}, and vice versa.
|
||||
* In effect, this method is similar to {@link #includes(MediaType)}, except that it's symmetric.
|
||||
* In effect, this method is similar to {@link #includes(MediaType)}, except that it <b>is</b> symmetric.
|
||||
* @param other the reference media type with which to compare
|
||||
* @return <code>true</code> if this media type is compatible with the given media type; <code>false</code> otherwise
|
||||
*/
|
||||
@@ -444,7 +453,7 @@ public class MediaType implements Comparable<MediaType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this {@link MediaType} to another alphabetically.
|
||||
* Compares this {@code MediaType} to another alphabetically.
|
||||
* @param other media type to compare to
|
||||
* @see #sortBySpecificity(List)
|
||||
*/
|
||||
@@ -533,7 +542,7 @@ public class MediaType implements Comparable<MediaType> {
|
||||
|
||||
|
||||
/**
|
||||
* Parse the given String value into a {@link MediaType} object,
|
||||
* Parse the given String value into a {@code MediaType} object,
|
||||
* with this method name following the 'valueOf' naming convention
|
||||
* (as supported by {@link org.springframework.core.convert.ConversionService}.
|
||||
* @see #parseMediaType(String)
|
||||
@@ -543,7 +552,7 @@ public class MediaType implements Comparable<MediaType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given String into a single {@link MediaType}.
|
||||
* Parse the given String into a single {@code MediaType}.
|
||||
* @param mediaType the string to parse
|
||||
* @return the media type
|
||||
* @throws IllegalArgumentException if the string cannot be parsed
|
||||
@@ -586,7 +595,7 @@ public class MediaType implements Comparable<MediaType> {
|
||||
|
||||
|
||||
/**
|
||||
* Parse the given, comma-seperated string into a list of {@link MediaType} objects.
|
||||
* Parse the given, comma-separated string into a list of {@code MediaType} objects.
|
||||
* <p>This method can be used to parse an Accept or Content-Type header.
|
||||
* @param mediaTypes the string to parse
|
||||
* @return the list of media types
|
||||
@@ -605,7 +614,7 @@ public class MediaType implements Comparable<MediaType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string representation of the given list of {@link MediaType} objects.
|
||||
* Return a string representation of the given list of {@code MediaType} objects.
|
||||
* <p>This method can be used to for an {@code Accept} or {@code Content-Type} header.
|
||||
* @param mediaTypes the string to parse
|
||||
* @return the list of media types
|
||||
@@ -624,7 +633,7 @@ public class MediaType implements Comparable<MediaType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the given list of {@link MediaType} objects by specificity.
|
||||
* Sorts the given list of {@code MediaType} objects by specificity.
|
||||
* <p>Given two media types:
|
||||
* <ol>
|
||||
* <li>if either media type has a {@linkplain #isWildcardType() wildcard type}, then the media type without the
|
||||
@@ -657,7 +666,7 @@ public class MediaType implements Comparable<MediaType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the given list of {@link MediaType} objects by quality value.
|
||||
* Sorts the given list of {@code MediaType} objects by quality value.
|
||||
* <p>Given two media types:
|
||||
* <ol>
|
||||
* <li>if the two media types have different {@linkplain #getQualityValue() quality value}, then the media type
|
||||
@@ -684,7 +693,10 @@ public class MediaType implements Comparable<MediaType> {
|
||||
}
|
||||
|
||||
|
||||
static final Comparator<MediaType> SPECIFICITY_COMPARATOR = new Comparator<MediaType>() {
|
||||
/**
|
||||
* Comparator used by {@link #sortBySpecificity(List)}.
|
||||
*/
|
||||
public static final Comparator<MediaType> SPECIFICITY_COMPARATOR = new Comparator<MediaType>() {
|
||||
|
||||
public int compare(MediaType mediaType1, MediaType mediaType2) {
|
||||
if (mediaType1.isWildcardType() && !mediaType2.isWildcardType()) { // */* < audio/*
|
||||
@@ -724,7 +736,10 @@ public class MediaType implements Comparable<MediaType> {
|
||||
};
|
||||
|
||||
|
||||
static final Comparator<MediaType> QUALITY_VALUE_COMPARATOR = new Comparator<MediaType>() {
|
||||
/**
|
||||
* Comparator used by {@link #sortByQualityValue(List)}.
|
||||
*/
|
||||
public static final Comparator<MediaType> QUALITY_VALUE_COMPARATOR = new Comparator<MediaType>() {
|
||||
|
||||
public int compare(MediaType mediaType1, MediaType mediaType2) {
|
||||
double quality1 = mediaType1.getQualityValue();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -19,13 +19,11 @@ package org.springframework.http.converter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -43,38 +41,28 @@ import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HttpMessageConverter} that can handle form data, including multipart form data
|
||||
* (i.e. file uploads).
|
||||
* Implementation of {@link HttpMessageConverter} that can handle form data, including multipart form data (i.e. file
|
||||
* uploads).
|
||||
*
|
||||
* <p>This converter can write the {@code application/x-www-form-urlencoded} and {@code multipart/form-data} media
|
||||
* types, and read the {@code application/x-www-form-urlencoded}) media type (but not {@code multipart/form-data}).
|
||||
*
|
||||
* <p>In other words, this converter can read and write 'normal' HTML forms (as
|
||||
* {@link MultiValueMap MultiValueMap<String, String>}), and it can write multipart form (as
|
||||
* {@link MultiValueMap MultiValueMap<String, Object>}. When writing multipart, this converter uses other
|
||||
* {@link HttpMessageConverter HttpMessageConverters} to write the respective MIME parts. By default, basic converters
|
||||
* are registered (supporting {@code Strings} and {@code Resources}, for instance); these can be overridden by setting
|
||||
* the {@link #setPartConverters(java.util.List) partConverters} property.
|
||||
* <p>In other words, this converter can read and write 'normal' HTML forms (as {@link MultiValueMap
|
||||
* MultiValueMap<String, String>}), and it can write multipart form (as {@link MultiValueMap
|
||||
* MultiValueMap<String, Object>}. When writing multipart, this converter uses other {@link HttpMessageConverter
|
||||
* HttpMessageConverters} to write the respective MIME parts. By default, basic converters are registered (supporting
|
||||
* {@code Strings} and {@code Resources}, for instance); these can be overridden by setting the {@link
|
||||
* #setPartConverters(java.util.List) partConverters} property.
|
||||
*
|
||||
* <p>For example, the following snippet shows how to submit an HTML form:
|
||||
* <pre class="code">
|
||||
* RestTemplate template = new RestTemplate(); // FormHttpMessageConverter is configured by default
|
||||
* MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();
|
||||
* form.add("field 1", "value 1");
|
||||
* form.add("field 2", "value 2");
|
||||
* form.add("field 2", "value 3");
|
||||
* template.postForLocation("http://example.com/myForm", form);
|
||||
* </pre>
|
||||
* <p>The following snippet shows how to do a file upload:
|
||||
* <pre class="code">
|
||||
* MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
|
||||
* parts.add("field 1", "value 1");
|
||||
* parts.add("file", new ClassPathResource("myFile.jpg"));
|
||||
* template.postForLocation("http://example.com/myFileUpload", parts);
|
||||
* </pre>
|
||||
* <p>For example, the following snippet shows how to submit an HTML form: <pre class="code"> RestTemplate template =
|
||||
* new RestTemplate(); // FormHttpMessageConverter is configured by default MultiValueMap<String, String> form =
|
||||
* new LinkedMultiValueMap<String, String>(); form.add("field 1", "value 1"); form.add("field 2", "value 2");
|
||||
* form.add("field 2", "value 3"); template.postForLocation("http://example.com/myForm", form); </pre> <p>The following
|
||||
* snippet shows how to do a file upload: <pre class="code"> MultiValueMap<String, Object> parts = new
|
||||
* LinkedMultiValueMap<String, Object>(); parts.add("field 1", "value 1"); parts.add("file", new
|
||||
* ClassPathResource("myFile.jpg")); template.postForLocation("http://example.com/myFileUpload", parts); </pre>
|
||||
*
|
||||
* <p>Some methods in this class were inspired by {@link org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity}.
|
||||
*
|
||||
@@ -88,16 +76,21 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
||||
new byte[]{'-', '_', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
||||
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A',
|
||||
'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
|
||||
'V', 'W', 'X', 'Y', 'Z'};
|
||||
'V', 'W', 'X', 'Y', 'Z'};
|
||||
|
||||
private final Random rnd = new Random();
|
||||
|
||||
private Charset charset = Charset.forName(WebUtils.DEFAULT_CHARACTER_ENCODING);
|
||||
private Charset charset = Charset.forName("UTF-8");
|
||||
|
||||
private List<MediaType> supportedMediaTypes = new ArrayList<MediaType>();
|
||||
|
||||
private List<HttpMessageConverter<?>> partConverters = new ArrayList<HttpMessageConverter<?>>();
|
||||
|
||||
|
||||
public FormHttpMessageConverter() {
|
||||
this.supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
this.supportedMediaTypes.add(MediaType.MULTIPART_FORM_DATA);
|
||||
|
||||
this.partConverters.add(new ByteArrayHttpMessageConverter());
|
||||
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
|
||||
stringHttpMessageConverter.setWriteAcceptCharset(false);
|
||||
@@ -106,14 +99,6 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a message body converter. Such a converters is used to convert objects to MIME parts.
|
||||
*/
|
||||
public final void addPartConverter(HttpMessageConverter<?> partConverter) {
|
||||
Assert.notNull(partConverter, "'partConverter' must not be NULL");
|
||||
this.partConverters.add(partConverter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message body converters to use. These converters are used to convert objects to MIME parts.
|
||||
*/
|
||||
@@ -122,6 +107,14 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
||||
this.partConverters = partConverters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a message body converter. Such a converters is used to convert objects to MIME parts.
|
||||
*/
|
||||
public final void addPartConverter(HttpMessageConverter<?> partConverter) {
|
||||
Assert.notNull(partConverter, "'partConverter' must not be NULL");
|
||||
this.partConverters.add(partConverter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the character set used for writing form data.
|
||||
*/
|
||||
@@ -129,34 +122,47 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
|
||||
public boolean canRead(Class<?> clazz, MediaType mediaType) {
|
||||
if (!MultiValueMap.class.isAssignableFrom(clazz)) {
|
||||
return false;
|
||||
}
|
||||
if (mediaType != null) {
|
||||
return MediaType.APPLICATION_FORM_URLENCODED.includes(mediaType);
|
||||
}
|
||||
else {
|
||||
if (mediaType == null) {
|
||||
return true;
|
||||
}
|
||||
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
|
||||
// we can't read multipart
|
||||
if (!supportedMediaType.equals(MediaType.MULTIPART_FORM_DATA) &&
|
||||
supportedMediaType.includes(mediaType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
|
||||
if (!MultiValueMap.class.isAssignableFrom(clazz)) {
|
||||
return false;
|
||||
}
|
||||
if (mediaType != null) {
|
||||
return mediaType.isCompatibleWith(MediaType.APPLICATION_FORM_URLENCODED) ||
|
||||
mediaType.isCompatibleWith(MediaType.MULTIPART_FORM_DATA);
|
||||
}
|
||||
else {
|
||||
if (mediaType == null || MediaType.ALL.equals(mediaType)) {
|
||||
return true;
|
||||
}
|
||||
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
|
||||
if (supportedMediaType.isCompatibleWith(mediaType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of {@link MediaType} objects supported by this converter.
|
||||
*/
|
||||
public void setSupportedMediaTypes(List<MediaType> supportedMediaTypes) {
|
||||
this.supportedMediaTypes = supportedMediaTypes;
|
||||
}
|
||||
|
||||
public List<MediaType> getSupportedMediaTypes() {
|
||||
return Arrays.asList(MediaType.APPLICATION_FORM_URLENCODED, MediaType.MULTIPART_FORM_DATA);
|
||||
return Collections.unmodifiableList(this.supportedMediaTypes);
|
||||
}
|
||||
|
||||
public MultiValueMap<String, String> read(Class<? extends MultiValueMap<String, ?>> clazz,
|
||||
@@ -188,7 +194,7 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
||||
public void write(MultiValueMap<String, ?> map, MediaType contentType, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
if (!isMultipart(map, contentType)) {
|
||||
writeForm((MultiValueMap<String, String>) map, outputMessage);
|
||||
writeForm((MultiValueMap<String, String>) map, contentType, outputMessage);
|
||||
}
|
||||
else {
|
||||
writeMultipart((MultiValueMap<String, Object>) map, outputMessage);
|
||||
@@ -209,8 +215,17 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
||||
return false;
|
||||
}
|
||||
|
||||
private void writeForm(MultiValueMap<String, String> form, HttpOutputMessage outputMessage) throws IOException {
|
||||
outputMessage.getHeaders().setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
private void writeForm(MultiValueMap<String, String> form, MediaType contentType, HttpOutputMessage outputMessage)
|
||||
throws IOException {
|
||||
Charset charset;
|
||||
if (contentType != null) {
|
||||
outputMessage.getHeaders().setContentType(contentType);
|
||||
charset = contentType.getCharSet() != null ? contentType.getCharSet() : this.charset;
|
||||
}
|
||||
else {
|
||||
outputMessage.getHeaders().setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
charset = this.charset;
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (Iterator<String> nameIterator = form.keySet().iterator(); nameIterator.hasNext();) {
|
||||
String name = nameIterator.next();
|
||||
@@ -229,10 +244,13 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
||||
builder.append('&');
|
||||
}
|
||||
}
|
||||
FileCopyUtils.copy(builder.toString(), new OutputStreamWriter(outputMessage.getBody(), charset));
|
||||
byte[] bytes = builder.toString().getBytes(charset.name());
|
||||
outputMessage.getHeaders().setContentLength(bytes.length);
|
||||
FileCopyUtils.copy(bytes, outputMessage.getBody());
|
||||
}
|
||||
|
||||
private void writeMultipart(MultiValueMap<String, Object> parts, HttpOutputMessage outputMessage) throws IOException {
|
||||
private void writeMultipart(MultiValueMap<String, Object> parts, HttpOutputMessage outputMessage)
|
||||
throws IOException {
|
||||
byte[] boundary = generateMultipartBoundary();
|
||||
|
||||
Map<String, String> parameters = Collections.singletonMap("boundary", new String(boundary, "US-ASCII"));
|
||||
@@ -310,7 +328,8 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
||||
|
||||
/**
|
||||
* Generate a multipart boundary.
|
||||
* <p>Default implementation returns a random boundary. Can be overridden in subclasses.
|
||||
* <p>The default implementation returns a random boundary.
|
||||
* Can be overridden in subclasses.
|
||||
*/
|
||||
protected byte[] generateMultipartBoundary() {
|
||||
byte[] boundary = new byte[rnd.nextInt(11) + 30];
|
||||
@@ -321,9 +340,10 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the filename of the given multipart part. This value will be used for the {@code Content-Disposition} header.
|
||||
* <p>Default implementation returns {@link Resource#getFilename()} if the part is a {@code Resource}, and
|
||||
* {@code null} in other cases. Can be overridden in subclasses.
|
||||
* Return the filename of the given multipart part. This value will be used for the
|
||||
* {@code Content-Disposition} header.
|
||||
* <p>The default implementation returns {@link Resource#getFilename()} if the part is a
|
||||
* {@code Resource}, and {@code null} in other cases. Can be overridden in subclasses.
|
||||
* @param part the part to determine the file name for
|
||||
* @return the filename, or {@code null} if not known
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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,11 +16,20 @@
|
||||
|
||||
package org.springframework.http.server;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -35,14 +44,22 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class ServletServerHttpRequest implements ServerHttpRequest {
|
||||
|
||||
private static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
|
||||
|
||||
private static final String POST_METHOD = "POST";
|
||||
|
||||
private static final String PUT_METHOD = "PUT";
|
||||
|
||||
private static final String FORM_CHARSET = "UTF-8";
|
||||
|
||||
private final HttpServletRequest servletRequest;
|
||||
|
||||
private HttpHeaders headers;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new instance of the ServletServerHttpRequest based on the given {@link HttpServletRequest}
|
||||
* @param servletRequest the HttpServletRequest
|
||||
* Construct a new instance of the ServletServerHttpRequest based on the given {@link HttpServletRequest}.
|
||||
* @param servletRequest the servlet request
|
||||
*/
|
||||
public ServletServerHttpRequest(HttpServletRequest servletRequest) {
|
||||
Assert.notNull(servletRequest, "'servletRequest' must not be null");
|
||||
@@ -50,15 +67,22 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@code HttpServletRequest} this object is based on.
|
||||
*/
|
||||
public HttpServletRequest getServletRequest() {
|
||||
return this.servletRequest;
|
||||
}
|
||||
|
||||
public HttpMethod getMethod() {
|
||||
return HttpMethod.valueOf(this.servletRequest.getMethod());
|
||||
}
|
||||
|
||||
public URI getURI() {
|
||||
try {
|
||||
return new URI(servletRequest.getScheme(), null, servletRequest.getServerName(),
|
||||
servletRequest.getServerPort(), servletRequest.getRequestURI(),
|
||||
servletRequest.getQueryString(), null);
|
||||
return new URI(this.servletRequest.getScheme(), null, this.servletRequest.getServerName(),
|
||||
this.servletRequest.getServerPort(), this.servletRequest.getRequestURI(),
|
||||
this.servletRequest.getQueryString(), null);
|
||||
}
|
||||
catch (URISyntaxException ex) {
|
||||
throw new IllegalStateException("Could not get HttpServletRequest URI: " + ex.getMessage(), ex);
|
||||
@@ -70,7 +94,8 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
|
||||
this.headers = new HttpHeaders();
|
||||
for (Enumeration headerNames = this.servletRequest.getHeaderNames(); headerNames.hasMoreElements();) {
|
||||
String headerName = (String) headerNames.nextElement();
|
||||
for (Enumeration headerValues = this.servletRequest.getHeaders(headerName); headerValues.hasMoreElements();) {
|
||||
for (Enumeration headerValues = this.servletRequest.getHeaders(headerName);
|
||||
headerValues.hasMoreElements();) {
|
||||
String headerValue = (String) headerValues.nextElement();
|
||||
this.headers.add(headerName, headerValue);
|
||||
}
|
||||
@@ -80,7 +105,45 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
|
||||
}
|
||||
|
||||
public InputStream getBody() throws IOException {
|
||||
return this.servletRequest.getInputStream();
|
||||
if (isFormSubmittal(this.servletRequest)) {
|
||||
return getFormBody(this.servletRequest);
|
||||
}
|
||||
else {
|
||||
return this.servletRequest.getInputStream();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFormSubmittal(HttpServletRequest request) {
|
||||
return FORM_CONTENT_TYPE.equals(request.getContentType()) &&
|
||||
(POST_METHOD.equalsIgnoreCase(request.getMethod()) || PUT_METHOD.equalsIgnoreCase(request.getMethod()));
|
||||
}
|
||||
|
||||
private InputStream getFormBody(HttpServletRequest request) throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
Writer writer = new OutputStreamWriter(bos, FORM_CHARSET);
|
||||
|
||||
Map<String, String[]> form = request.getParameterMap();
|
||||
for (Iterator<String> nameIterator = form.keySet().iterator(); nameIterator.hasNext();) {
|
||||
String name = nameIterator.next();
|
||||
List<String> values = Arrays.asList(form.get(name));
|
||||
for (Iterator<String> valueIterator = values.iterator(); valueIterator.hasNext();) {
|
||||
String value = valueIterator.next();
|
||||
writer.write(URLEncoder.encode(name, FORM_CHARSET));
|
||||
if (value != null) {
|
||||
writer.write('=');
|
||||
writer.write(URLEncoder.encode(value, FORM_CHARSET));
|
||||
if (valueIterator.hasNext()) {
|
||||
writer.write('&');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nameIterator.hasNext()) {
|
||||
writer.append('&');
|
||||
}
|
||||
}
|
||||
writer.flush();
|
||||
|
||||
return new ByteArrayInputStream(bos.toByteArray());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -43,7 +43,7 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
|
||||
|
||||
/**
|
||||
* Construct a new instance of the ServletServerHttpResponse based on the given {@link HttpServletResponse}.
|
||||
* @param servletResponse the HTTP Servlet response
|
||||
* @param servletResponse the servlet response
|
||||
*/
|
||||
public ServletServerHttpResponse(HttpServletResponse servletResponse) {
|
||||
Assert.notNull(servletResponse, "'servletResponse' must not be null");
|
||||
@@ -51,12 +51,19 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the {@code HttpServletResponse} this object is based on.
|
||||
*/
|
||||
public HttpServletResponse getServletResponse() {
|
||||
return this.servletResponse;
|
||||
}
|
||||
|
||||
public void setStatusCode(HttpStatus status) {
|
||||
this.servletResponse.setStatus(status.value());
|
||||
}
|
||||
|
||||
public HttpHeaders getHeaders() {
|
||||
return headersWritten ? HttpHeaders.readOnlyHttpHeaders(headers) : this.headers;
|
||||
return (this.headersWritten ? HttpHeaders.readOnlyHttpHeaders(this.headers) : this.headers);
|
||||
}
|
||||
|
||||
public OutputStream getBody() throws IOException {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -21,6 +21,7 @@ import javax.servlet.ServletRequest;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.multipart.MultipartRequest;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Special {@link org.springframework.validation.DataBinder} to perform data binding
|
||||
@@ -103,8 +104,8 @@ public class ServletRequestDataBinder extends WebDataBinder {
|
||||
*/
|
||||
public void bind(ServletRequest request) {
|
||||
MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
|
||||
if (request instanceof MultipartRequest) {
|
||||
MultipartRequest multipartRequest = (MultipartRequest) request;
|
||||
MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
|
||||
if (multipartRequest != null) {
|
||||
bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
|
||||
}
|
||||
doBind(mpvs);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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,
|
||||
@@ -20,10 +20,6 @@ import java.security.Principal;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletRequestWrapper;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.ServletResponseWrapper;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
@@ -31,6 +27,7 @@ import javax.servlet.http.HttpSession;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* {@link WebRequest} adapter for an {@link javax.servlet.http.HttpServletRequest}.
|
||||
@@ -40,10 +37,16 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class ServletWebRequest extends ServletRequestAttributes implements NativeWebRequest {
|
||||
|
||||
private static final String HEADER_ETAG = "ETag";
|
||||
|
||||
private static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
|
||||
|
||||
private static final String HEADER_IF_NONE_MATCH = "If-None-Match";
|
||||
|
||||
private static final String HEADER_LAST_MODIFIED = "Last-Modified";
|
||||
|
||||
private static final String METHOD_GET = "GET";
|
||||
|
||||
|
||||
private HttpServletResponse response;
|
||||
|
||||
@@ -86,40 +89,12 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getNativeRequest(Class<T> requiredType) {
|
||||
if (requiredType != null) {
|
||||
ServletRequest request = getRequest();
|
||||
while (request != null) {
|
||||
if (requiredType.isInstance(request)) {
|
||||
return (T) request;
|
||||
}
|
||||
else if (request instanceof ServletRequestWrapper) {
|
||||
request = ((ServletRequestWrapper) request).getRequest();
|
||||
}
|
||||
else {
|
||||
request = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return WebUtils.getNativeRequest(getRequest(), requiredType);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getNativeResponse(Class<T> requiredType) {
|
||||
if (requiredType != null) {
|
||||
ServletResponse response = getResponse();
|
||||
while (response != null) {
|
||||
if (requiredType.isInstance(response)) {
|
||||
return (T) response;
|
||||
}
|
||||
else if (response instanceof ServletResponseWrapper) {
|
||||
response = ((ServletResponseWrapper) response).getResponse();
|
||||
}
|
||||
else {
|
||||
response = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return WebUtils.getNativeResponse(getResponse(), requiredType);
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +161,7 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
|
||||
long ifModifiedSince = getRequest().getDateHeader(HEADER_IF_MODIFIED_SINCE);
|
||||
this.notModified = (ifModifiedSince >= (lastModifiedTimestamp / 1000 * 1000));
|
||||
if (this.response != null) {
|
||||
if (this.notModified && "GET".equals(getRequest().getMethod())) {
|
||||
if (this.notModified && METHOD_GET.equals(getRequest().getMethod())) {
|
||||
this.response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -69,7 +69,6 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("ETag [" + responseETag + "] equal to If-None-Match, sending 304");
|
||||
}
|
||||
response.setContentLength(0);
|
||||
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||
}
|
||||
else {
|
||||
@@ -89,8 +88,8 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
|
||||
}
|
||||
|
||||
private void copyBodyToResponse(byte[] body, HttpServletResponse response) throws IOException {
|
||||
response.setContentLength(body.length);
|
||||
if (body.length > 0) {
|
||||
response.setContentLength(body.length);
|
||||
FileCopyUtils.copy(body, response.getOutputStream());
|
||||
}
|
||||
}
|
||||
@@ -113,7 +112,7 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
|
||||
/**
|
||||
* Generate the ETag header value from the given response body byte array.
|
||||
* <p>The default implementation generates an MD5 hash.
|
||||
* @param bytes the response bdoy as byte array
|
||||
* @param bytes the response body as byte array
|
||||
* @return the ETag header value
|
||||
* @see org.springframework.util.DigestUtils
|
||||
*/
|
||||
@@ -168,6 +167,10 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
|
||||
this.statusCode = sc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentLength(int len) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletOutputStream getOutputStream() {
|
||||
return this.outputStream;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -23,6 +23,9 @@ import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletRequestWrapper;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.ServletResponseWrapper;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@@ -366,6 +369,48 @@ public abstract class WebUtils {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return an appropriate request object of the specified type, if available,
|
||||
* unwrapping the given request as far as necessary.
|
||||
* @param request the servlet request to introspect
|
||||
* @param requiredType the desired type of request object
|
||||
* @return the matching request object, or <code>null</code> if none
|
||||
* of that type is available
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getNativeRequest(ServletRequest request, Class<T> requiredType) {
|
||||
if (requiredType != null) {
|
||||
if (requiredType.isInstance(request)) {
|
||||
return (T) request;
|
||||
}
|
||||
else if (request instanceof ServletRequestWrapper) {
|
||||
return getNativeRequest(((ServletRequestWrapper) request).getRequest(), requiredType);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an appropriate response object of the specified type, if available,
|
||||
* unwrapping the given response as far as necessary.
|
||||
* @param response the servlet response to introspect
|
||||
* @param requiredType the desired type of response object
|
||||
* @return the matching response object, or <code>null</code> if none
|
||||
* of that type is available
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getNativeResponse(ServletResponse response, Class<T> requiredType) {
|
||||
if (requiredType != null) {
|
||||
if (requiredType.isInstance(response)) {
|
||||
return (T) response;
|
||||
}
|
||||
else if (response instanceof ServletResponseWrapper) {
|
||||
return getNativeResponse(((ServletResponseWrapper) response).getResponse(), requiredType);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given request is an include request,
|
||||
* that is, not a top-level HTTP request coming in from the outside.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -30,7 +30,6 @@ import org.apache.commons.fileupload.FileItemFactory;
|
||||
import org.apache.commons.fileupload.FileUpload;
|
||||
import org.apache.commons.fileupload.RequestContext;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -45,6 +44,8 @@ import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
@@ -60,13 +61,13 @@ public class FormHttpMessageConverterTests {
|
||||
@Test
|
||||
public void canRead() {
|
||||
assertTrue(converter.canRead(MultiValueMap.class, new MediaType("application", "x-www-form-urlencoded")));
|
||||
assertFalse(converter.canRead(MultiValueMap.class, new MediaType("multipart","form-data")));
|
||||
assertFalse(converter.canRead(MultiValueMap.class, new MediaType("multipart", "form-data")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canWrite() {
|
||||
assertTrue(converter.canWrite(MultiValueMap.class, new MediaType("application", "x-www-form-urlencoded")));
|
||||
assertTrue(converter.canWrite(MultiValueMap.class, new MediaType("multipart","form-data")));
|
||||
assertTrue(converter.canWrite(MultiValueMap.class, new MediaType("multipart", "form-data")));
|
||||
assertTrue(converter.canWrite(MultiValueMap.class, MediaType.ALL));
|
||||
}
|
||||
|
||||
@@ -77,7 +78,7 @@ public class FormHttpMessageConverterTests {
|
||||
Charset iso88591 = Charset.forName("ISO-8859-1");
|
||||
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(iso88591));
|
||||
inputMessage.getHeaders().setContentType(new MediaType("application", "x-www-form-urlencoded", iso88591));
|
||||
MultiValueMap<String, String> result = (MultiValueMap<String, String>) converter.read(null, inputMessage);
|
||||
MultiValueMap<String, String> result = converter.read(null, inputMessage);
|
||||
|
||||
assertEquals("Invalid result", 3, result.size());
|
||||
assertEquals("Invalid result", "value 1", result.getFirst("name 1"));
|
||||
@@ -97,19 +98,21 @@ public class FormHttpMessageConverterTests {
|
||||
body.add("name 3", null);
|
||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||
converter.write(body, MediaType.APPLICATION_FORM_URLENCODED, outputMessage);
|
||||
Charset iso88591 = Charset.forName("ISO-8859-1");
|
||||
assertEquals("Invalid result", "name+1=value+1&name+2=value+2%2B1&name+2=value+2%2B2&name+3",
|
||||
outputMessage.getBodyAsString(iso88591));
|
||||
outputMessage.getBodyAsString(Charset.forName("UTF-8")));
|
||||
assertEquals("Invalid content-type", new MediaType("application", "x-www-form-urlencoded"),
|
||||
outputMessage.getHeaders().getContentType());
|
||||
assertEquals("Invalid content-length", outputMessage.getBodyAsBytes().length,
|
||||
outputMessage.getHeaders().getContentLength());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void writeMultipart() throws Exception {
|
||||
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
|
||||
parts.add("name 1", "value 1");
|
||||
parts.add("name 2", "value 2+1");
|
||||
parts.add("name 2", "value 2+2");
|
||||
|
||||
Resource logo = new ClassPathResource("/org/springframework/http/converter/logo.jpg");
|
||||
parts.add("logo", logo);
|
||||
Source xml = new StreamSource(new StringReader("<root><child/></root>"));
|
||||
@@ -122,7 +125,7 @@ public class FormHttpMessageConverterTests {
|
||||
converter.write(parts, MediaType.MULTIPART_FORM_DATA, outputMessage);
|
||||
|
||||
final MediaType contentType = outputMessage.getHeaders().getContentType();
|
||||
assertNotNull(contentType.getParameter("boundary"));
|
||||
assertNotNull("No boundary found", contentType.getParameter("boundary"));
|
||||
|
||||
// see if Commons FileUpload can read what we wrote
|
||||
FileItemFactory fileItemFactory = new DiskFileItemFactory();
|
||||
@@ -157,6 +160,7 @@ public class FormHttpMessageConverterTests {
|
||||
}
|
||||
|
||||
private static class MockHttpOutputMessageRequestContext implements RequestContext {
|
||||
|
||||
private final MockHttpOutputMessage outputMessage;
|
||||
|
||||
private MockHttpOutputMessageRequestContext(MockHttpOutputMessage outputMessage) {
|
||||
@@ -182,5 +186,4 @@ public class FormHttpMessageConverterTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user