INT-1044 added support for form data Maps in POST requests

This commit is contained in:
Mark Fisher
2010-04-15 19:20:18 +00:00
parent 0c47f9a0b4
commit e99f649aa5
2 changed files with 228 additions and 6 deletions

View File

@@ -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.
@@ -20,12 +20,18 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.springframework.integration.core.Message;
@@ -107,6 +113,7 @@ public class DefaultOutboundRequestMapper implements OutboundRequestMapper {
return this.createRequestFromMessage(message, url, requestMethod);
}
@SuppressWarnings("unchecked")
private HttpRequest createRequestFromPayload(Object payload, URL url, String requestMethod) throws Exception {
ByteArrayOutputStream requestBody = new ByteArrayOutputStream();
String contentType = null;
@@ -160,6 +167,7 @@ public class DefaultOutboundRequestMapper implements OutboundRequestMapper {
return parameterMap;
}
@SuppressWarnings("unchecked")
private String writeToRequestBody(Object object, ByteArrayOutputStream byteStream) throws Exception {
String contentType = null;
if (object instanceof byte[]) {
@@ -170,17 +178,89 @@ public class DefaultOutboundRequestMapper implements OutboundRequestMapper {
byteStream.write(((String) object).getBytes(this.charset));
contentType = "text/plain; charset=" + this.charset;
}
else if (object instanceof Serializable) {
byteStream.write(this.serializeObject((Serializable) object));
contentType = "application/x-java-serialized-object";
}
else {
if (object instanceof Map && isFormData((Map) object)) {
byte[] data = this.formDataAsBytes((Map) object);
if (data != null) {
byteStream.write(data);
contentType = "application/x-www-form-urlencoded";
}
}
if (contentType == null && object instanceof Serializable) {
byteStream.write(this.serializeObject((Serializable) object));
contentType = "application/x-java-serialized-object";
}
}
if (contentType == null) {
throw new IllegalArgumentException("payload must be a byte array, " +
"String, or Serializable object for a 'POST' or 'PUT' request");
"String, Map, or Serializable object for a 'POST' or 'PUT' request");
}
return contentType;
}
/**
* If all keys are Strings, we'll consider the Map to be form data.
*/
@SuppressWarnings("unchecked")
private boolean isFormData(Map map) {
for (Object key : map.keySet()) {
if (!(key instanceof String)) {
return false;
}
}
return true;
}
@SuppressWarnings("unchecked")
private byte[] formDataAsBytes(Map form) throws UnsupportedEncodingException {
StringBuilder builder = new StringBuilder();
Iterator<?> nameIterator = form.keySet().iterator();
while (nameIterator.hasNext()) {
Object next = nameIterator.next();
Assert.isTrue(next instanceof String, "Form map keys must be Strings.");
String name = (String) next;
Object value = form.get(name);
if (value == null) {
builder.append(URLEncoder.encode(name, this.charset));
}
else {
List<String> values = null;
if (value instanceof String) {
values = Collections.singletonList((String) value);
}
else if (value instanceof String[]) {
values = Arrays.asList((String[]) value);
}
else {
if (!(value instanceof Iterable)) {
return null;
}
Iterator iterator = ((Iterable) value).iterator();
values = new ArrayList<String>();
while (iterator.hasNext()) {
Object nextValue = iterator.next();
if (!(nextValue instanceof String)) {
return null;
}
values.add((String) nextValue);
}
}
Iterator<String> valueIterator = values.iterator();
builder.append(URLEncoder.encode(name, this.charset));
while (valueIterator.hasNext()) {
builder.append('=' + URLEncoder.encode(valueIterator.next(), this.charset));
if (valueIterator.hasNext()) {
builder.append('&' + URLEncoder.encode(name, this.charset));
}
}
}
if (nameIterator.hasNext()) {
builder.append('&');
}
}
return builder.toString().getBytes(this.charset);
}
private byte[] serializeObject(Serializable object) throws IOException {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
ObjectOutputStream objectStream = new ObjectOutputStream(byteStream);

View File

@@ -0,0 +1,142 @@
/*
* 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.
* You may obtain a copy of the License at
*
* 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,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.http;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.springframework.integration.core.Message;
import org.springframework.integration.message.MessageBuilder;
/**
* @author Mark Fisher
*/
public class DefaultOutboundRequestMapperTests {
@Test
public void simpleStringValueFormData() throws Exception {
DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org"));
Map<String, String> form = new LinkedHashMap<String, String>();
form.put("a", "1");
form.put("b", "2");
form.put("c", "3");
Message<?> message = MessageBuilder.withPayload(form).build();
HttpRequest request = mapper.fromMessage(message);
String bodyText = request.getBody().toString("UTF-8");
assertEquals("a=1&b=2&c=3", bodyText);
assertEquals("application/x-www-form-urlencoded", request.getContentType());
}
@Test
@SuppressWarnings("unchecked")
public void stringArrayValueFormData() throws Exception {
DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org"));
Map form = new LinkedHashMap();
form.put("a", new String[] { "1", "2", "3" });
form.put("b", "4");
form.put("c", new String[] { "5" });
form.put("d", "6");
Message<?> message = MessageBuilder.withPayload(form).build();
HttpRequest request = mapper.fromMessage(message);
String bodyText = request.getBody().toString("UTF-8");
assertEquals("a=1&a=2&a=3&b=4&c=5&d=6", bodyText);
assertEquals("application/x-www-form-urlencoded", request.getContentType());
}
@Test
@SuppressWarnings("unchecked")
public void stringListValueFormData() throws Exception {
DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org"));
Map form = new LinkedHashMap();
List<String> listA = new ArrayList<String>();
listA.add("1");
listA.add("2");
form.put("a", listA);
form.put("b", Collections.EMPTY_LIST);
form.put("c", Collections.singletonList("3"));
Message<?> message = MessageBuilder.withPayload(form).build();
HttpRequest request = mapper.fromMessage(message);
String bodyText = request.getBody().toString("UTF-8");
assertEquals("a=1&a=2&b&c=3", bodyText);
assertEquals("application/x-www-form-urlencoded", request.getContentType());
}
@Test
@SuppressWarnings("unchecked")
public void nameOnlyWithNullValues() throws Exception {
DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org"));
Map form = new LinkedHashMap();
form.put("a", null);
form.put("b", "foo");
form.put("c", null);
Message<?> message = MessageBuilder.withPayload(form).build();
HttpRequest request = mapper.fromMessage(message);
String bodyText = request.getBody().toString("UTF-8");
assertEquals("a&b=foo&c", bodyText);
assertEquals("application/x-www-form-urlencoded", request.getContentType());
}
@Test
public void encodedFormData() throws Exception {
DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org"));
Map<String, String> form = new LinkedHashMap<String, String>();
form.put("a", "1 + 2 + 3");
form.put("b", "4+5");
form.put("c", "97%");
Message<?> message = MessageBuilder.withPayload(form).build();
HttpRequest request = mapper.fromMessage(message);
String bodyText = request.getBody().toString("UTF-8");
assertEquals("a=1+%2B+2+%2B+3&b=4%2B5&c=97%25", bodyText);
assertEquals("application/x-www-form-urlencoded", request.getContentType());
}
@Test
@SuppressWarnings("unchecked")
public void nonFormDataInMap() throws Exception {
DefaultOutboundRequestMapper mapper = new DefaultOutboundRequestMapper(new URL("http://example.org"));
Map<String, TestBean> form = new LinkedHashMap<String, TestBean>();
form.put("A", new TestBean());
form.put("B", new TestBean());
Message<?> message = MessageBuilder.withPayload(form).build();
HttpRequest request = mapper.fromMessage(message);
byte[] body = request.getBody().toByteArray();
ByteArrayInputStream byteStream = new ByteArrayInputStream(body);
Object result = new ObjectInputStream(byteStream).readObject();
assertEquals(LinkedHashMap.class, result.getClass());
Map<String, TestBean> resultMap = (Map<String, TestBean>) result;
assertEquals(2, resultMap.size());
assertEquals(TestBean.class, resultMap.get("A").getClass());
assertEquals(TestBean.class, resultMap.get("B").getClass());
}
@SuppressWarnings("serial")
private static class TestBean implements Serializable {
}
}