Skip to content

Commit

Permalink
Ensure that MessageBodyWriter is passed the proper media type
Browse files Browse the repository at this point in the history
Fixes: #41354
  • Loading branch information
geoand committed Jun 25, 2024
1 parent 32d0c2d commit 001e3f6
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import jakarta.ws.rs.ext.MessageBodyWriter;

import org.jboss.resteasy.reactive.common.util.MediaTypeHelper;
import org.jboss.resteasy.reactive.common.util.ServerMediaType;
import org.jboss.resteasy.reactive.server.core.EncodedMediaType;
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext;
import org.jboss.resteasy.reactive.server.core.ServerSerialisers;
Expand Down Expand Up @@ -41,22 +42,27 @@ public void write(ResteasyReactiveRequestContext context, Object entity) throws
ServerHttpRequest vertxRequest = context.serverRequest();
// first check and see if the resource method defined a media type and try to use it
if ((context.getTarget() != null) && (context.getTarget().getProduces() != null)) {
ServerMediaType producesServerMediaType = context.getTarget().getProduces();
MediaType negotiatedMediaType = null;
List<String> accepts = context.getHttpHeaders().getRequestHeader(HttpHeaders.ACCEPT);
for (String accept : accepts) {
negotiatedMediaType = context.getTarget().getProduces().negotiateProduces(accept).getKey();
negotiatedMediaType = producesServerMediaType.negotiateProduces(accept).getKey();
if (negotiatedMediaType != null) {
break;
}
}
if (negotiatedMediaType == null) { // fallback to ensure that MessageBodyWriter is passed the proper media type
negotiatedMediaType = producesServerMediaType
.negotiateProduces(vertxRequest.getRequestHeader(HttpHeaders.ACCEPT)).getKey();
}

List<MessageBodyWriter<?>> writersList = serialisers.findWriters(null, entity.getClass(), negotiatedMediaType,
RuntimeType.SERVER);
if (!writersList.isEmpty()) {
writers = writersList.toArray(EMPTY_ARRAY);
// use the actual type the method declares as this is what the spec expects despite the fact that we might
// have used the suffix of the subtype to determine a MessageBodyWriter
MediaType[] sortedOriginalMediaTypes = context.getTarget().getProduces().getSortedOriginalMediaTypes();
MediaType[] sortedOriginalMediaTypes = producesServerMediaType.getSortedOriginalMediaTypes();
for (MediaType methodMediaType : sortedOriginalMediaTypes) {
if (methodMediaType.isCompatible(negotiatedMediaType)) {
selectedMediaType = methodMediaType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti
break;
}
}
if (res == null) { // fallback for some tests
if (res == null) { // fallback to ensure that MessageBodyWriter is passed the proper media type
res = mediaTypeList.negotiateProduces(requestContext.serverRequest().getRequestHeader(HttpHeaders.ACCEPT))
.getKey();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package org.jboss.resteasy.reactive.server.vertx.test.matching;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.function.Supplier;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.MessageBodyWriter;
import jakarta.ws.rs.ext.Provider;

import org.jboss.resteasy.reactive.server.vertx.test.framework.ResteasyReactiveUnitTest;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.restassured.http.Header;

public class StringMessageBodyWriterTest {

@RegisterExtension
static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest()
.setArchiveProducer(new Supplier<>() {
@Override
public JavaArchive get() {
return ShrinkWrap.create(JavaArchive.class);
}
});

@Test
void testHelloEndpoint() {
given()
.when().get("/hello")
.then()
.statusCode(200)
.body(is("Greeting response: Hello from Quarkus REST"));
}

@Test
void testWithNoAcceptHeader() {
// Prevent RestAssured from setting any Accept header
final var header = new Header("Accept", null);

given()
.when()
.header(header)
.get("/hello")
.then()
.statusCode(200)
.body(is("Greeting response: Hello from Quarkus REST"));
}

@Path("/hello")
public static class GreetingResource {

@GET
@Produces(MediaType.TEXT_PLAIN)
public Response hello() {
return Response.ok("Hello from Quarkus REST").build();
}
}

@Provider
public static class GreetingMessageBodyWriter implements MessageBodyWriter<String> {

@Override
public boolean isWriteable(final Class<?> aClass, final Type type, final Annotation[] annotations,
final MediaType mediaType) {
return String.class.isAssignableFrom(aClass) && MediaType.TEXT_PLAIN_TYPE.isCompatible(mediaType);
}

@Override
public void writeTo(final String s, final Class<?> aClass, final Type type, final Annotation[] annotations,
final MediaType mediaType,
final MultivaluedMap<String, Object> multivaluedMap, final OutputStream outputStream)
throws IOException, WebApplicationException {

final var content = "Greeting response: " + s;
outputStream.write(content.getBytes());
}
}
}

0 comments on commit 001e3f6

Please sign in to comment.