提交 34d84ec3 编写于 作者: J Jake Wharton

Allow overriding 'Content-Type' header.

This 'Request' objects passed to a client will now have their header list pre-populated with 'Content-Type' and 'Content-Length' headers set. Previously the 'Client' implementations were responsibile for setting this data.
上级 67839e7a
......@@ -48,6 +48,7 @@ final class RequestBuilder implements RequestInterceptor.RequestFacade {
private String relativeUrl;
private StringBuilder queryParams;
private List<Header> headers;
private boolean hasContentTypeHeader;
RequestBuilder(String apiUrl, RestMethodInfo methodInfo, Converter converter) {
this.apiUrl = apiUrl;
......@@ -62,6 +63,7 @@ final class RequestBuilder implements RequestInterceptor.RequestFacade {
if (methodInfo.headers != null) {
headers = new ArrayList<Header>(methodInfo.headers);
}
hasContentTypeHeader = methodInfo.hasContentTypeHeader;
relativeUrl = methodInfo.requestUrl;
......@@ -100,6 +102,10 @@ final class RequestBuilder implements RequestInterceptor.RequestFacade {
this.headers = headers = new ArrayList<Header>(2);
}
headers.add(new Header(name, value));
if ("Content-Type".equalsIgnoreCase(name)) {
hasContentTypeHeader = true;
}
}
@Override public void addPathParam(String name, String value) {
......@@ -309,6 +315,20 @@ final class RequestBuilder implements RequestInterceptor.RequestFacade {
url.append(queryParams);
}
TypedOutput body = this.body;
if (body != null) {
// Only add Content-Type header from the body if one is not already set.
if (!hasContentTypeHeader) {
addHeader("Content-Type", body.mimeType());
}
// Only add Content-Length header from the body if it is known.
long length = body.length();
if (length != -1) {
addHeader("Content-Length", String.valueOf(length));
}
}
return new Request(requestMethod, url.toString(), headers, body);
}
}
......@@ -444,14 +444,6 @@ public class RestAdapter {
TypedOutput body = request.getBody();
if (body != null) {
bodySize = body.length();
String bodyMime = body.mimeType();
if (bodyMime != null) {
log.log("Content-Type: " + bodyMime);
}
if (bodySize != -1) {
log.log("Content-Length: " + bodySize);
}
if (logLevel.ordinal() >= LogLevel.FULL.ordinal()) {
if (!request.getHeaders().isEmpty()) {
......@@ -465,7 +457,7 @@ public class RestAdapter {
byte[] bodyBytes = ((TypedByteArray) body).getBytes();
bodySize = bodyBytes.length;
String bodyCharset = MimeUtil.parseCharset(bodyMime);
String bodyCharset = MimeUtil.parseCharset(body.mimeType());
log.log(new String(bodyBytes, bodyCharset));
}
}
......
......@@ -97,6 +97,7 @@ final class RestMethodInfo {
Set<String> requestUrlParamNames;
String requestQuery;
List<retrofit.client.Header> headers;
boolean hasContentTypeHeader;
// Parameter-level details
String[] requestParamNames;
......@@ -221,7 +222,7 @@ final class RestMethodInfo {
requestQuery = query;
}
private List<retrofit.client.Header> parseHeaders(String[] headers) {
List<retrofit.client.Header> parseHeaders(String[] headers) {
List<retrofit.client.Header> headerList = new ArrayList<retrofit.client.Header>();
for (String header : headers) {
int colon = header.indexOf(':');
......@@ -229,8 +230,12 @@ final class RestMethodInfo {
throw methodError("@Headers value must be in the form \"Name: Value\". Found: \"%s\"",
header);
}
headerList.add(new retrofit.client.Header(header.substring(0, colon),
header.substring(colon + 1).trim()));
String headerName = header.substring(0, colon);
String headerValue = header.substring(colon + 1).trim();
if ("Content-Type".equalsIgnoreCase(headerName)) {
hasContentTypeHeader = true;
}
headerList.add(new retrofit.client.Header(headerName, headerValue));
}
return headerList;
}
......@@ -372,6 +377,10 @@ final class RestMethodInfo {
paramNames[i] = name;
paramUsage[i] = ParamUsage.HEADER;
if ("Content-Type".equalsIgnoreCase(name)) {
hasContentTypeHeader = true;
}
} else if (annotationType == Field.class) {
if (requestType != RequestType.FORM_URL_ENCODED) {
throw parameterError(i, "@Field parameters can only be used with form encoding.");
......
......@@ -71,12 +71,6 @@ public class UrlFetchClient implements Client {
TypedOutput body = request.getBody();
if (body != null) {
fetchRequest.setHeader(new HTTPHeader("Content-Type", body.mimeType()));
long length = body.length();
if (length != -1) {
fetchRequest.setHeader(new HTTPHeader("Content-Length", String.valueOf(length)));
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
body.writeTo(baos);
fetchRequest.setPayload(baos.toByteArray());
......
......@@ -131,7 +131,6 @@ public class ApacheClient implements Client {
TypedOutputEntity(TypedOutput typedOutput) {
this.typedOutput = typedOutput;
setContentType(typedOutput.mimeType());
}
@Override public boolean isRepeatable() {
......
......@@ -25,6 +25,10 @@ public interface Client {
/**
* Synchronously execute an HTTP represented by {@code request} and encapsulate all response data
* into a {@link Response} instance.
* <p>
* Note: If the request has a body, its length and mime type will have already been added to the
* header list as {@code Content-Length} and {@code Content-Type}, respectively. Do NOT alter
* these values as they might have been set as a result of an application-level configuration.
*/
Response execute(Request request) throws IOException;
......
......@@ -57,11 +57,9 @@ public class UrlConnectionClient implements Client {
TypedOutput body = request.getBody();
if (body != null) {
connection.setDoOutput(true);
connection.addRequestProperty("Content-Type", body.mimeType());
long length = body.length();
if (length != -1) {
connection.setFixedLengthStreamingMode((int) length);
connection.addRequestProperty("Content-Length", String.valueOf(length));
} else {
connection.setChunkedStreamingMode(CHUNK_SIZE);
}
......
......@@ -7,6 +7,7 @@ import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
......@@ -420,7 +421,9 @@ public class RequestBuilderTest {
.setBody(Arrays.asList("quick", "brown", "fox")) //
.build();
assertThat(request.getMethod()).isEqualTo("POST");
assertThat(request.getHeaders()).isEmpty();
assertThat(request.getHeaders()).containsExactly(
new Header("Content-Type", "application/json; charset=UTF-8"),
new Header("Content-Length", "23"));
assertThat(request.getUrl()).isEqualTo("http://example.com/foo/bar/");
assertTypedBytes(request.getBody(), "[\"quick\",\"brown\",\"fox\"]");
}
......@@ -451,7 +454,9 @@ public class RequestBuilderTest {
.addPathParam("kit", "kat") //
.build();
assertThat(request.getMethod()).isEqualTo("POST");
assertThat(request.getHeaders()).isEmpty();
assertThat(request.getHeaders()).containsExactly(
new Header("Content-Type", "application/json; charset=UTF-8"),
new Header("Content-Length", "23"));
assertThat(request.getUrl()).isEqualTo("http://example.com/foo/bar/pong/kat/");
assertTypedBytes(request.getBody(), "[\"quick\",\"brown\",\"fox\"]");
}
......@@ -467,7 +472,9 @@ public class RequestBuilderTest {
.addPart("kit", new TypedString("kat")) //
.build();
assertThat(request.getMethod()).isEqualTo("POST");
assertThat(request.getHeaders()).isEmpty();
assertThat(request.getHeaders()).hasSize(2);
assertThat(request.getHeaders().get(0).getName()).isEqualTo("Content-Type");
assertThat(request.getHeaders().get(1)).isEqualTo(new Header("Content-Length", "414"));
assertThat(request.getUrl()).isEqualTo("http://example.com/foo/bar/");
MultipartTypedOutput body = (MultipartTypedOutput) request.getBody();
......@@ -494,7 +501,9 @@ public class RequestBuilderTest {
.addPart("fizz", null) //
.build();
assertThat(request.getMethod()).isEqualTo("POST");
assertThat(request.getHeaders()).isEmpty();
assertThat(request.getHeaders()).hasSize(2);
assertThat(request.getHeaders().get(0).getName()).isEqualTo("Content-Type");
assertThat(request.getHeaders().get(1)).isEqualTo(new Header("Content-Length", "228"));
assertThat(request.getUrl()).isEqualTo("http://example.com/foo/bar/");
MultipartTypedOutput body = (MultipartTypedOutput) request.getBody();
......@@ -617,8 +626,7 @@ public class RequestBuilderTest {
.setMethod("GET") //
.setUrl("http://example.com") //
.setPath("/foo/bar/") //
.addHeader("ping", "pong") //
.addHeader("kit", "kat") //
.addHeaders("ping: pong", "kit: kat") //
.build();
assertThat(request.getMethod()).isEqualTo("GET");
assertThat(request.getHeaders()) //
......@@ -647,7 +655,7 @@ public class RequestBuilderTest {
.setMethod("GET") //
.setUrl("http://example.com") //
.setPath("/foo/bar/") //
.addHeader("ping", "pong") //
.addHeaders("ping: pong") //
.addInterceptorHeader("kit", "kat") //
.build();
assertThat(request.getMethod()).isEqualTo("GET");
......@@ -662,7 +670,7 @@ public class RequestBuilderTest {
.setMethod("GET") //
.setUrl("http://example.com") //
.setPath("/foo/bar/") //
.addHeader("ping", "pong") //
.addHeaders("ping: pong") //
.addInterceptorHeader("kit", "kat") //
.addHeaderParam("fizz", "buzz") //
.build();
......@@ -678,7 +686,7 @@ public class RequestBuilderTest {
.setMethod("GET") //
.setUrl("http://example.com") //
.setPath("/foo/bar/") //
.addHeader("ping", "pong") //
.addHeaders("ping: pong") //
.addHeaderParam("kit", "kat") //
.build();
assertThat(request.getMethod()).isEqualTo("GET");
......@@ -693,7 +701,7 @@ public class RequestBuilderTest {
.setMethod("GET") //
.setUrl("http://example.com") //
.setPath("/foo/bar/") //
.addHeader("ping", "pong") //
.addHeaders("ping: pong") //
.addHeaderParam("kit", "kat") //
.build();
assertThat(request.getMethod()).isEqualTo("GET");
......@@ -712,6 +720,32 @@ public class RequestBuilderTest {
assertThat(request.getUrl()).isEqualTo("http://example.com/foo/bar/");
}
@Test public void contentTypeAnnotationHeaderOverrides() throws Exception {
Request request = new Helper() //
.setMethod("POST") //
.setUrl("http://example.com") //
.setPath("/") //
.addHeaders("Content-Type: text/not-plain") //
.setBody(new TypedString("Plain")) //
.build();
assertThat(request.getHeaders()) //
.containsExactly(new Header("Content-Type", "text/not-plain"),
new Header("Content-Length", "5"));
}
@Test public void contentTypeParameterHeaderOverrides() throws Exception {
Request request = new Helper() //
.setMethod("POST") //
.setUrl("http://example.com") //
.setPath("/") //
.addHeaderParam("Content-Type", "text/not-plain") //
.setBody(new TypedString("Plain")) //
.build();
assertThat(request.getHeaders()) //
.containsExactly(new Header("Content-Type", "text/not-plain"),
new Header("Content-Length", "5"));
}
private static void assertTypedBytes(TypedOutput bytes, String expected) throws IOException {
assertThat(bytes).isNotNull();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
......@@ -730,7 +764,7 @@ public class RequestBuilderTest {
private final List<String> paramNames = new ArrayList<String>();
private final List<ParamUsage> paramUsages = new ArrayList<ParamUsage>();
private final List<Object> args = new ArrayList<Object>();
private final List<Header> headers = new ArrayList<Header>();
private final List<String> headers = new ArrayList<String>();
private final List<Header> interceptorHeaders = new ArrayList<Header>();
private final Map<String, String> interceptorPathParams = new LinkedHashMap<String, String>();
private final Map<String, String> interceptorQueryParams = new LinkedHashMap<String, String>();
......@@ -838,8 +872,8 @@ public class RequestBuilderTest {
return this;
}
Helper addHeader(String name, String value) {
headers.add(new Header(name, value));
Helper addHeaders(String... headers) {
Collections.addAll(this.headers, headers);
return this;
}
......@@ -887,7 +921,7 @@ public class RequestBuilderTest {
methodInfo.requestQuery = query;
methodInfo.requestParamNames = paramNames.toArray(new String[paramNames.size()]);
methodInfo.requestParamUsage = paramUsages.toArray(new ParamUsage[paramUsages.size()]);
methodInfo.headers = headers;
methodInfo.headers = methodInfo.parseHeaders(headers.toArray(new String[headers.size()]));
methodInfo.loaded = true;
RequestBuilder requestBuilder = new RequestBuilder(url, methodInfo, GSON);
......
......@@ -48,9 +48,7 @@ public class UrlFetchClientTest {
assertThat(fetchRequest.getMethod()).isEqualTo(POST);
assertThat(fetchRequest.getURL().toString()).isEqualTo(HOST + "/foo/bar/");
List<HTTPHeader> fetchHeaders = fetchRequest.getHeaders();
assertThat(fetchHeaders).hasSize(2);
assertHeader(fetchHeaders.get(0), "Content-Type", "text/plain; charset=UTF-8");
assertHeader(fetchHeaders.get(1), "Content-Length", "2");
assertThat(fetchHeaders).hasSize(0);
assertBytes(fetchRequest.getPayload(), "hi");
}
......@@ -65,11 +63,7 @@ public class UrlFetchClientTest {
assertThat(fetchRequest.getMethod()).isEqualTo(POST);
assertThat(fetchRequest.getURL().toString()).isEqualTo(HOST + "/that/");
List<HTTPHeader> fetchHeaders = fetchRequest.getHeaders();
assertThat(fetchHeaders).hasSize(2);
HTTPHeader headerZero = fetchHeaders.get(0);
assertThat(headerZero.getName()).isEqualTo("Content-Type");
assertThat(headerZero.getValue()).startsWith("multipart/form-data; boundary=");
assertHeader(fetchHeaders.get(1), "Content-Length", String.valueOf(body.length()));
assertThat(fetchHeaders).hasSize(0);
assertThat(fetchRequest.getPayload()).isNotEmpty();
}
......
......@@ -56,7 +56,6 @@ public class ApacheClientTest {
HttpEntity entity = entityRequest.getEntity();
assertThat(entity).isNotNull();
assertBytes(ByteStreams.toByteArray(entity.getContent()), "hi");
assertThat(entity.getContentType().getValue()).isEqualTo("text/plain; charset=UTF-8");
}
@Test public void multipart() {
......
......@@ -46,10 +46,7 @@ public class UrlConnectionClientTest {
assertThat(connection.getRequestMethod()).isEqualTo("POST");
assertThat(connection.getURL().toString()).isEqualTo(HOST + "/foo/bar/");
assertThat(connection.getRequestProperties()).hasSize(2);
assertThat(connection.getRequestProperty("Content-Type")) //
.isEqualTo("text/plain; charset=UTF-8");
assertThat(connection.getRequestProperty("Content-Length")).isEqualTo("2");
assertThat(connection.getRequestProperties()).hasSize(0);
assertBytes(connection.getOutputStream().toByteArray(), "hi");
}
......@@ -67,9 +64,7 @@ public class UrlConnectionClientTest {
assertThat(connection.getRequestMethod()).isEqualTo("POST");
assertThat(connection.getURL().toString()).isEqualTo(HOST + "/that/");
assertThat(connection.getRequestProperties()).hasSize(2);
assertThat(connection.getRequestProperty("Content-Type")).startsWith("multipart/form-data;");
assertThat(connection.getRequestProperty("Content-Length")).isEqualTo(String.valueOf(output.length));
assertThat(connection.getRequestProperties()).hasSize(0);
assertThat(output.length).isGreaterThan(0);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册