Allow to specify request body type in RestTemplate

This commit allows to specify the request body type in order to
serialize generic types with a GenericHttpMessageConverter if
needed.

Issue: SPR-13154
This commit is contained in:
Sebastien Deleuze
2016-02-22 15:00:49 +01:00
parent 7b1fcfc7c3
commit 3329abffc8
5 changed files with 165 additions and 12 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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,6 +16,7 @@
package org.springframework.http;
import java.lang.reflect.Type;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Arrays;
@@ -54,6 +55,7 @@ import org.springframework.util.ObjectUtils;
* </pre>
*
* @author Arjen Poutsma
* @author Sebastien Deleuze
* @since 4.1
* @see #getMethod()
* @see #getUrl()
@@ -64,6 +66,8 @@ public class RequestEntity<T> extends HttpEntity<T> {
private final URI url;
private final Type type;
/**
* Constructor with method and URL but without body nor headers.
@@ -81,7 +85,19 @@ public class RequestEntity<T> extends HttpEntity<T> {
* @param url the URL
*/
public RequestEntity(T body, HttpMethod method, URI url) {
this(body, null, method, url);
this(body, null, method, url, null);
}
/**
* Constructor with method, URL, body and type but without headers.
* @param body the body
* @param method the method
* @param url the URL
* @param type the type used for generic type resolution
* @since 4.3
*/
public RequestEntity(T body, HttpMethod method, URI url, Type type) {
this(body, null, method, url, type);
}
/**
@@ -91,7 +107,7 @@ public class RequestEntity<T> extends HttpEntity<T> {
* @param url the URL
*/
public RequestEntity(MultiValueMap<String, String> headers, HttpMethod method, URI url) {
this(null, headers, method, url);
this(null, headers, method, url, null);
}
/**
@@ -102,9 +118,23 @@ public class RequestEntity<T> extends HttpEntity<T> {
* @param url the URL
*/
public RequestEntity(T body, MultiValueMap<String, String> headers, HttpMethod method, URI url) {
this(body, headers, method, url, null);
}
/**
* Constructor with method, URL, headers, body and type.
* @param body the body
* @param headers the headers
* @param method the method
* @param url the URL
* @param type the type used for generic type resolution
* @since 4.3
*/
public RequestEntity(T body, MultiValueMap<String, String> headers, HttpMethod method, URI url, Type type) {
super(body, headers);
this.method = method;
this.url = url;
this.type = type;
}
@@ -124,6 +154,14 @@ public class RequestEntity<T> extends HttpEntity<T> {
return this.url;
}
/**
* Return the type of the request's body.
* @return the request's body type
* @since 4.3
*/
public Type getType() {
return (this.type == null && this.getBody() != null ? this.getBody().getClass() : this.type );
}
@Override
public boolean equals(Object other) {
@@ -327,6 +365,16 @@ public class RequestEntity<T> extends HttpEntity<T> {
* @return the built request entity
*/
<T> RequestEntity<T> body(T body);
/**
* Set the body and type of the request entity and build the RequestEntity.
* @param <T> the type of the body
* @param body the body of the request entity
* @param type the type of the body, useful for generic type resolution
* @return the built request entity
* @since 4.3
*/
<T> RequestEntity<T> body(T body, Type type);
}
@@ -396,6 +444,11 @@ public class RequestEntity<T> extends HttpEntity<T> {
public <T> RequestEntity<T> body(T body) {
return new RequestEntity<T>(body, this.headers, this.method, this.url);
}
@Override
public <T> RequestEntity<T> body(T body, Type type) {
return new RequestEntity<T>(body, this.headers, this.method, this.url, type);
}
}
}

View File

@@ -782,11 +782,34 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
}
else {
Object requestBody = this.requestEntity.getBody();
Class<?> requestType = requestBody.getClass();
Class<?> requestBodyClass = requestBody.getClass();
Type requestBodyType = (this.requestEntity instanceof RequestEntity ?
((RequestEntity<?>)this.requestEntity).getType() : requestBodyClass);
HttpHeaders requestHeaders = this.requestEntity.getHeaders();
MediaType requestContentType = requestHeaders.getContentType();
for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
if (messageConverter.canWrite(requestType, requestContentType)) {
if (messageConverter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<Object> genericMessageConverter = (GenericHttpMessageConverter<Object>) messageConverter;
if (genericMessageConverter.canWrite(requestBodyType, requestBodyClass, requestContentType)) {
if (!requestHeaders.isEmpty()) {
httpRequest.getHeaders().putAll(requestHeaders);
}
if (logger.isDebugEnabled()) {
if (requestContentType != null) {
logger.debug("Writing [" + requestBody + "] as \"" + requestContentType +
"\" using [" + messageConverter + "]");
}
else {
logger.debug("Writing [" + requestBody + "] using [" + messageConverter + "]");
}
}
genericMessageConverter.write(
requestBody, requestBodyType, requestContentType, httpRequest);
return;
}
}
else if (messageConverter.canWrite(requestBodyClass, requestContentType)) {
if (!requestHeaders.isEmpty()) {
httpRequest.getHeaders().putAll(requestHeaders);
}
@@ -806,7 +829,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
}
}
String message = "Could not write request: no suitable HttpMessageConverter found for request type [" +
requestType.getName() + "]";
requestBodyClass.getName() + "]";
if (requestContentType != null) {
message += " and content type [" + requestContentType + "]";
}