Skip to content

Added support for "application/*+x-protobuf" media type #34645

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;

Expand All @@ -55,8 +54,8 @@
*
* <p>To generate {@code Message} Java classes, you need to install the {@code protoc} binary.
*
* <p>This converter supports by default {@code "application/x-protobuf"} and {@code "text/plain"}
* with the official {@code "com.google.protobuf:protobuf-java"} library.
* <p>This converter supports by default {@code "application/x-protobuf"}, {@code "application/*+x-protobuf"}
* and {@code "text/plain"} with the official {@code "com.google.protobuf:protobuf-java"} library.
* The {@code "application/json"} format is also supported with the {@code "com.google.protobuf:protobuf-java-util"}
* dependency. See {@link ProtobufJsonFormatHttpMessageConverter} for a configurable variant.
*
Expand All @@ -66,6 +65,7 @@
* @author Brian Clozel
* @author Juergen Hoeller
* @author Sebastien Deleuze
* @author Kamil Doroszkiewicz
* @since 4.1
* @see JsonFormat
* @see ProtobufJsonFormatHttpMessageConverter
Expand All @@ -82,6 +82,11 @@ public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter<M
*/
public static final MediaType PROTOBUF = new MediaType("application", "x-protobuf", DEFAULT_CHARSET);

/**
* The media-type for protobuf {@code application/*+x-protobuf}.
*/
public static final MediaType PLUS_PROTOBUF = new MediaType("application", "*+x-protobuf", DEFAULT_CHARSET);

/**
* The HTTP header containing the protobuf schema.
*/
Expand Down Expand Up @@ -132,7 +137,7 @@ else if (protobufJsonFormatPresent) {
}

setSupportedMediaTypes(Arrays.asList(this.protobufFormatSupport != null ?
this.protobufFormatSupport.supportedMediaTypes() : new MediaType[] {PROTOBUF, TEXT_PLAIN}));
this.protobufFormatSupport.supportedMediaTypes() : new MediaType[] {PROTOBUF, PLUS_PROTOBUF, TEXT_PLAIN}));

this.extensionRegistry = (extensionRegistry == null ? ExtensionRegistry.newInstance() : extensionRegistry);
}
Expand Down Expand Up @@ -162,7 +167,8 @@ protected Message readInternal(Class<? extends Message> clazz, HttpInputMessage
}

Message.Builder builder = getMessageBuilder(clazz);
if (PROTOBUF.isCompatibleWith(contentType)) {
if (PROTOBUF.isCompatibleWith(contentType) ||
PLUS_PROTOBUF.isCompatibleWith(contentType)) {
builder.mergeFrom(inputMessage.getBody(), this.extensionRegistry);
}
else if (TEXT_PLAIN.isCompatibleWith(contentType)) {
Expand Down Expand Up @@ -209,14 +215,14 @@ protected void writeInternal(Message message, HttpOutputMessage outputMessage)
MediaType contentType = outputMessage.getHeaders().getContentType();
if (contentType == null) {
contentType = getDefaultContentType(message);
Assert.state(contentType != null, "No content type");
}
Charset charset = contentType.getCharset();
if (charset == null) {
charset = DEFAULT_CHARSET;
}

if (PROTOBUF.isCompatibleWith(contentType)) {
if (PROTOBUF.isCompatibleWith(contentType) ||
PLUS_PROTOBUF.isCompatibleWith(contentType)) {
setProtoHeader(outputMessage, message);
CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputMessage.getBody());
message.writeTo(codedOutputStream);
Expand Down Expand Up @@ -285,7 +291,7 @@ public ProtobufJavaUtilSupport(JsonFormat.@Nullable Parser parser, JsonFormat.@N

@Override
public MediaType[] supportedMediaTypes() {
return new MediaType[] {PROTOBUF, TEXT_PLAIN, APPLICATION_JSON};
return new MediaType[] {PROTOBUF, PLUS_PROTOBUF, TEXT_PLAIN, APPLICATION_JSON};
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,14 @@ class ProtobufHttpMessageConverterTests {
private ExtensionRegistry extensionRegistry = mock();

private Msg testMsg = Msg.newBuilder().setFoo("Foo").setBlah(SecondMsg.newBuilder().setBlah(123).build()).build();
private MediaType testPlusProtoMediaType = MediaType.parseMediaType("application/vnd.example.public.v1+x-protobuf");


@Test
void canRead() {
assertThat(this.converter.canRead(Msg.class, null)).isTrue();
assertThat(this.converter.canRead(Msg.class, ProtobufHttpMessageConverter.PROTOBUF)).isTrue();
assertThat(this.converter.canRead(Msg.class, this.testPlusProtoMediaType)).isTrue();
assertThat(this.converter.canRead(Msg.class, MediaType.APPLICATION_JSON)).isTrue();
assertThat(this.converter.canRead(Msg.class, MediaType.TEXT_PLAIN)).isTrue();
}
Expand All @@ -62,6 +64,7 @@ void canRead() {
void canWrite() {
assertThat(this.converter.canWrite(Msg.class, null)).isTrue();
assertThat(this.converter.canWrite(Msg.class, ProtobufHttpMessageConverter.PROTOBUF)).isTrue();
assertThat(this.converter.canRead(Msg.class, this.testPlusProtoMediaType)).isTrue();
assertThat(this.converter.canWrite(Msg.class, MediaType.APPLICATION_JSON)).isTrue();
assertThat(this.converter.canWrite(Msg.class, MediaType.TEXT_PLAIN)).isTrue();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,14 @@ class ProtobufJsonFormatHttpMessageConverterTests {
JsonFormat.parser(), JsonFormat.printer());

private final Msg testMsg = Msg.newBuilder().setFoo("Foo").setBlah(SecondMsg.newBuilder().setBlah(123).build()).build();
private MediaType testPlusProtoMediaType = MediaType.parseMediaType("application/vnd.examle.public.v1+x-protobuf");


@Test
void canRead() {
assertThat(this.converter.canRead(Msg.class, null)).isTrue();
assertThat(this.converter.canRead(Msg.class, ProtobufHttpMessageConverter.PROTOBUF)).isTrue();
assertThat(this.converter.canRead(Msg.class, this.testPlusProtoMediaType)).isTrue();
assertThat(this.converter.canRead(Msg.class, MediaType.APPLICATION_JSON)).isTrue();
assertThat(this.converter.canRead(Msg.class, MediaType.TEXT_PLAIN)).isTrue();
}
Expand All @@ -56,6 +58,7 @@ void canRead() {
void canWrite() {
assertThat(this.converter.canWrite(Msg.class, null)).isTrue();
assertThat(this.converter.canWrite(Msg.class, ProtobufHttpMessageConverter.PROTOBUF)).isTrue();
assertThat(this.converter.canWrite(Msg.class, this.testPlusProtoMediaType)).isTrue();
assertThat(this.converter.canWrite(Msg.class, MediaType.APPLICATION_JSON)).isTrue();
assertThat(this.converter.canWrite(Msg.class, MediaType.TEXT_PLAIN)).isTrue();
}
Expand Down