Skip to content

Commit f534c34

Browse files
committed
Follow standard REST-JSON content-type handling
Update the REST-JSON marshalling logic to conform to the standard expected behavior WRT to the `Content-Type` of the request.
1 parent 0c0f39e commit f534c34

File tree

5 files changed

+35
-33
lines changed

5 files changed

+35
-33
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"category": "AWS SDK for Java v2",
3+
"contributor": "",
4+
"type": "bugfix",
5+
"description": "Update the REST-JSON marshalling logic to conform to the standard expected behavior WRT to the `Content-Type` of the request."
6+
}

core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/DefaultJsonContentTypeResolver.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
*/
2424
@SdkProtectedApi
2525
public class DefaultJsonContentTypeResolver implements JsonContentTypeResolver {
26+
private static final String REST_JSON_CONTENT_TYPE = "application/json";
2627

2728
private final String prefix;
2829

@@ -32,7 +33,9 @@ public DefaultJsonContentTypeResolver(String prefix) {
3233

3334
@Override
3435
public String resolveContentType(AwsJsonProtocolMetadata protocolMetadata) {
35-
//Changing this to 'application/json' may break clients expecting 'application/x-amz-json-1.1'
36+
if (AwsJsonProtocol.REST_JSON.equals(protocolMetadata.protocol())) {
37+
return REST_JSON_CONTENT_TYPE;
38+
}
3639
return prefix + protocolMetadata.protocolVersion();
3740
}
3841
}

core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonProtocolMarshaller.java

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package software.amazon.awssdk.protocols.json.internal.marshall;
1717

1818
import static software.amazon.awssdk.core.internal.util.Mimetype.MIMETYPE_EVENT_STREAM;
19-
import static software.amazon.awssdk.core.protocol.MarshallingType.DOCUMENT;
2019
import static software.amazon.awssdk.http.Header.CHUNKED;
2120
import static software.amazon.awssdk.http.Header.CONTENT_LENGTH;
2221
import static software.amazon.awssdk.http.Header.CONTENT_TYPE;
@@ -176,25 +175,30 @@ private void startMarshalling() {
176175
void doMarshall(SdkPojo pojo) {
177176
for (SdkField<?> field : pojo.sdkFields()) {
178177
Object val = field.getValueOrDefault(pojo);
179-
if (isBinary(field, val)) {
180-
request.contentStreamProvider(((SdkBytes) val)::asInputStream);
181-
} else if (isDocumentType(field) && val != null) {
182-
marshalDocumentType(field, val);
183-
} else {
184-
if (val != null && field.containsTrait(PayloadTrait.class)) {
185-
jsonGenerator.writeStartObject();
186-
doMarshall((SdkPojo) val);
187-
jsonGenerator.writeEndObject();
188-
} else {
189-
MARSHALLER_REGISTRY.getMarshaller(field.location(), field.marshallingType(), val)
190-
.marshall(val, marshallerContext, field.locationName(), (SdkField<Object>) field);
178+
if (isExplicitBinaryPayload(field)) {
179+
if (val != null) {
180+
request.contentStreamProvider(((SdkBytes) val)::asInputStream);
181+
}
182+
} else if (isExplicitPayloadMember(field)) {
183+
// Explicit JSON payloads are always marshalled as an object,
184+
// even if they're null, in which case it's an empty object.
185+
jsonGenerator.writeStartObject();
186+
if (val != null) {
187+
if (field.marshallingType().equals(MarshallingType.DOCUMENT)) {
188+
marshallField(field, val);
189+
} else {
190+
doMarshall((SdkPojo) val);
191+
}
191192
}
193+
jsonGenerator.writeEndObject();
194+
} else {
195+
marshallField(field, val);
192196
}
193197
}
194198
}
195199

196-
private boolean isBinary(SdkField<?> field, Object val) {
197-
return isExplicitPayloadMember(field) && val instanceof SdkBytes;
200+
private boolean isExplicitBinaryPayload(SdkField<?> field) {
201+
return isExplicitPayloadMember(field) && MarshallingType.SDK_BYTES.equals(field.marshallingType());
198202
}
199203

200204
private boolean isExplicitPayloadMember(SdkField<?> field) {
@@ -243,27 +247,16 @@ private SdkHttpFullRequest finishMarshalling() {
243247
}
244248
request.removeHeader(CONTENT_LENGTH);
245249
request.putHeader(TRANSFER_ENCODING, CHUNKED);
246-
} else if (contentType != null && !hasStreamingInput && request.contentStreamProvider() != null) {
250+
} else if (contentType != null && !hasStreamingInput && request.headers().containsKey(CONTENT_LENGTH)) {
247251
request.putHeader(CONTENT_TYPE, contentType);
248252
}
249253
}
250254

251255
return request.build();
252256
}
253257

254-
private boolean isDocumentType(SdkField<?> field) {
255-
return DOCUMENT.equals(field.marshallingType());
256-
}
257-
258-
private void marshalDocumentType(SdkField<?> field, Object val) {
259-
boolean isExplicitPayloadField = hasExplicitPayloadMember && field.containsTrait(PayloadTrait.class);
260-
if (isExplicitPayloadField) {
261-
jsonGenerator.writeStartObject();
262-
}
258+
private void marshallField(SdkField<?> field, Object val) {
263259
MARSHALLER_REGISTRY.getMarshaller(field.location(), field.marshallingType(), val)
264260
.marshall(val, marshallerContext, field.locationName(), (SdkField<Object>) field);
265-
if (isExplicitPayloadField) {
266-
jsonGenerator.writeEndObject();
267-
}
268261
}
269262
}

test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/documenttype/DocumentTypeTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ public void explicitDocumentOnlyNullPayload() {
223223
WithExplicitDocumentPayloadResponse response =
224224
jsonClient.withExplicitDocumentPayload(c -> c.myDocument(null).accept(AcceptHeader.IMAGE_JPEG));
225225
String syncRequest = getSyncRequestBody();
226-
assertThat(syncRequest).isEmpty();
226+
assertThat(syncRequest).isEqualTo("{}");
227227
SdkHttpRequest sdkHttpRequest = getSyncRequest();
228228
assertThat(sdkHttpRequest.firstMatchingHeader("accept").get()).contains(AcceptHeader.IMAGE_JPEG.toString());
229229
assertThat(response.myDocument()).isNull();
@@ -236,7 +236,7 @@ public void explicitDocumentOnlyEmptyPayload() {
236236
WithExplicitDocumentPayloadResponse response =
237237
jsonClient.withExplicitDocumentPayload(c -> c.myDocument(null).accept(AcceptHeader.IMAGE_JPEG));
238238
String syncRequest = getSyncRequestBody();
239-
assertThat(syncRequest).isEmpty();
239+
assertThat(syncRequest).isEqualTo("{}");
240240
SdkHttpRequest sdkHttpRequest = getSyncRequest();
241241
assertThat(sdkHttpRequest.firstMatchingHeader("accept").get()).contains(AcceptHeader.IMAGE_JPEG.toString());
242242
assertThat(response.myDocument()).isNull();

test/protocol-tests-core/src/main/resources/software/amazon/awssdk/protocol/suites/cases/rest-json-input.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
}
6060
},
6161
{
62-
"description": "Explicit payload member and no parameters marshalls into an empty request body",
62+
"description": "Explicit payload member and no parameters marshalls into an empty JSON object",
6363
"given": {
6464
"input": {
6565
}
@@ -71,7 +71,7 @@
7171
"then": {
7272
"serializedAs": {
7373
"body": {
74-
"equals": ""
74+
"jsonEquals": "{}"
7575
}
7676
}
7777
}

0 commit comments

Comments
 (0)