Skip to content

Commit

Permalink
Merge pull request #29825 from FroMage/22444
Browse files Browse the repository at this point in the history
Deprecate `@ServerRequestFilter(withBody)` in favour of `@WithFormRead`
  • Loading branch information
FroMage committed Dec 13, 2022
2 parents 4fe6585 + 04e20f6 commit f256c11
Show file tree
Hide file tree
Showing 18 changed files with 310 additions and 61 deletions.
Expand Up @@ -187,7 +187,7 @@ protected <T, B extends AbstractInterceptorBuildItem> void registerInterceptors(
if (filterItem instanceof ContainerRequestFilterBuildItem) {
ContainerRequestFilterBuildItem crfbi = (ContainerRequestFilterBuildItem) filterItem;
interceptor.setNonBlockingRequired(crfbi.isNonBlockingRequired());
interceptor.setReadBody(crfbi.isReadBody());
interceptor.setWithFormRead(crfbi.isWithFormRead());
MethodInfo filterSourceMethod = crfbi.getFilterSourceMethod();
if (filterSourceMethod != null) {
interceptor.metadata = Map.of(FILTER_SOURCE_METHOD_METADATA_KEY, filterSourceMethod);
Expand Down
Expand Up @@ -6,23 +6,23 @@ public final class ContainerRequestFilterBuildItem extends AbstractInterceptorBu

private final boolean preMatching;
private final boolean nonBlockingRequired;
private final boolean readBody;
private final boolean withFormRead;

private final MethodInfo filterSourceMethod;

protected ContainerRequestFilterBuildItem(Builder builder) {
super(builder);
this.preMatching = builder.preMatching;
this.nonBlockingRequired = builder.nonBlockingRequired;
this.readBody = builder.readBody;
this.withFormRead = builder.withFormRead;
this.filterSourceMethod = builder.filterSourceMethod;
}

public ContainerRequestFilterBuildItem(String className) {
super(className);
this.preMatching = false;
this.nonBlockingRequired = false;
this.readBody = false;
this.withFormRead = false;
this.filterSourceMethod = null;
}

Expand All @@ -34,8 +34,8 @@ public boolean isNonBlockingRequired() {
return nonBlockingRequired;
}

public boolean isReadBody() {
return readBody;
public boolean isWithFormRead() {
return withFormRead;
}

public MethodInfo getFilterSourceMethod() {
Expand All @@ -45,7 +45,7 @@ public MethodInfo getFilterSourceMethod() {
public static final class Builder extends AbstractInterceptorBuildItem.Builder<ContainerRequestFilterBuildItem, Builder> {
boolean preMatching = false;
boolean nonBlockingRequired = false;
boolean readBody = false;
boolean withFormRead = false;

MethodInfo filterSourceMethod = null;

Expand All @@ -63,8 +63,8 @@ public Builder setNonBlockingRequired(boolean nonBlockingRequired) {
return this;
}

public Builder setReadBody(boolean readBody) {
this.readBody = readBody;
public Builder setWithFormRead(boolean withFormRead) {
this.withFormRead = withFormRead;
return this;
}

Expand Down
Expand Up @@ -370,7 +370,7 @@ public void handleCustomAnnotatedMethods(
.setPriority(generated.getPriority())
.setPreMatching(generated.isPreMatching())
.setNonBlockingRequired(generated.isNonBlocking())
.setReadBody(generated.isReadBody())
.setWithFormRead(generated.isWithFormRead())
.setFilterSourceMethod(generated.getFilterSourceMethod());
if (!generated.getNameBindingNames().isEmpty()) {
builder.setNameBindingNames(generated.getNameBindingNames());
Expand Down
@@ -0,0 +1,119 @@
package io.quarkus.resteasy.reactive.server.test.customproviders;

import java.util.function.Supplier;

import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.HttpHeaders;

import org.hamcrest.Matchers;
import org.jboss.resteasy.reactive.RestForm;
import org.jboss.resteasy.reactive.RestQuery;
import org.jboss.resteasy.reactive.server.ServerRequestFilter;
import org.jboss.resteasy.reactive.server.WithFormRead;
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext;
import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveContainerRequestContext;
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.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;

public class ImpliedReadBodyRequestFilterTest {

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

@Test
public void testMethodWithBody() {
RestAssured.with()
.formParam("name", "Quarkus")
.post("/hello")
.then().body(Matchers.equalTo("hello Quarkus!!!!!!!"));
}

@Test
public void testMethodWithUndeclaredBody() {
RestAssured.with()
.formParam("name", "Quarkus")
.post("/hello/empty")
.then().body(Matchers.equalTo("hello !!!!!!!"));
}

@Test
public void testMethodWithStringBody() {
// make sure that a form-reading filter doesn't prevent non-form request bodies from being deserialised
RestAssured.with()
.formParam("name", "Quarkus")
.post("/hello/string")
.then().body(Matchers.equalTo("hello name=Quarkus!!!!!!!"));
RestAssured.with()
.body("Quarkus")
.post("/hello/string")
.then().body(Matchers.equalTo("hello Quarkus?"));
}

@Test
public void testMethodWithoutBody() {
RestAssured.with()
.queryParam("name", "Quarkus")
.get("/hello")
.then().body(Matchers.equalTo("hello Quarkus!"));
}

@Path("hello")
public static class HelloResource {

@POST
public String helloPost(@RestForm String name, HttpHeaders headers) {
return "hello " + name + headers.getHeaderString("suffix");
}

@Path("empty")
@POST
public String helloEmptyPost(HttpHeaders headers) {
return "hello " + headers.getHeaderString("suffix");
}

@Path("string")
@POST
public String helloStringPost(String body, HttpHeaders headers) {
return "hello " + body + headers.getHeaderString("suffix");
}

@GET
public String helloGet(@RestQuery String name, HttpHeaders headers) {
return "hello " + name + headers.getHeaderString("suffix");
}
}

public static class Filters {

@WithFormRead
@ServerRequestFilter
public void addSuffix(ResteasyReactiveContainerRequestContext containerRequestContext) {
ResteasyReactiveRequestContext rrContext = (ResteasyReactiveRequestContext) containerRequestContext
.getServerRequestContext();
if (containerRequestContext.getMethod().equals("POST")) {
String nameFormParam = (String) rrContext.getFormParameter("name", true, false);
if (nameFormParam != null) {
containerRequestContext.getHeaders().putSingle("suffix", "!".repeat(nameFormParam.length()));
} else {
containerRequestContext.getHeaders().putSingle("suffix", "?");
}
} else {
containerRequestContext.getHeaders().putSingle("suffix", "!");
}
}
}
}
Expand Up @@ -11,6 +11,7 @@
import org.jboss.resteasy.reactive.RestForm;
import org.jboss.resteasy.reactive.RestQuery;
import org.jboss.resteasy.reactive.server.ServerRequestFilter;
import org.jboss.resteasy.reactive.server.WithFormRead;
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext;
import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveContainerRequestContext;
import org.jboss.shrinkwrap.api.ShrinkWrap;
Expand Down Expand Up @@ -41,6 +42,27 @@ public void testMethodWithBody() {
.then().body(Matchers.equalTo("hello Quarkus!!!!!!!"));
}

@Test
public void testMethodWithUndeclaredBody() {
RestAssured.with()
.formParam("name", "Quarkus")
.post("/hello/empty")
.then().body(Matchers.equalTo("hello !!!!!!!"));
}

@Test
public void testMethodWithStringBody() {
// make sure that a form-reading filter doesn't prevent non-form request bodies from being deserialised
RestAssured.with()
.formParam("name", "Quarkus")
.post("/hello/string")
.then().body(Matchers.equalTo("hello name=Quarkus!!!!!!!"));
RestAssured.with()
.body("Quarkus")
.post("/hello/string")
.then().body(Matchers.equalTo("hello Quarkus?"));
}

@Test
public void testMethodWithoutBody() {
RestAssured.with()
Expand All @@ -57,6 +79,18 @@ public String helloPost(@RestForm String name, HttpHeaders headers) {
return "hello " + name + headers.getHeaderString("suffix");
}

@Path("empty")
@POST
public String helloEmptyPost(HttpHeaders headers) {
return "hello " + headers.getHeaderString("suffix");
}

@Path("string")
@POST
public String helloStringPost(String body, HttpHeaders headers) {
return "hello " + body + headers.getHeaderString("suffix");
}

@GET
public String helloGet(@RestQuery String name, HttpHeaders headers) {
return "hello " + name + headers.getHeaderString("suffix");
Expand All @@ -65,13 +99,18 @@ public String helloGet(@RestQuery String name, HttpHeaders headers) {

public static class Filters {

@WithFormRead
@ServerRequestFilter(readBody = true)
public void addSuffix(ResteasyReactiveContainerRequestContext containerRequestContext) {
ResteasyReactiveRequestContext rrContext = (ResteasyReactiveRequestContext) containerRequestContext
.getServerRequestContext();
if (containerRequestContext.getMethod().equals("POST")) {
String nameFormParam = (String) rrContext.getFormParameter("name", true, false);
containerRequestContext.getHeaders().putSingle("suffix", "!".repeat(nameFormParam.length()));
if (nameFormParam != null) {
containerRequestContext.getHeaders().putSingle("suffix", "!".repeat(nameFormParam.length()));
} else {
containerRequestContext.getHeaders().putSingle("suffix", "?");
}
} else {
containerRequestContext.getHeaders().putSingle("suffix", "!");
}
Expand Down
@@ -0,0 +1,64 @@
package io.quarkus.resteasy.reactive.server.test.customproviders;

import java.util.function.Supplier;

import javax.ws.rs.POST;
import javax.ws.rs.Path;

import org.hamcrest.Matchers;
import org.jboss.resteasy.reactive.RestForm;
import org.jboss.resteasy.reactive.server.WithFormRead;
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext;
import org.jboss.resteasy.reactive.server.spi.ServerRequestContext;
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.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;

public class WithFormBodyTest {

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

@Test
public void testMethodWithBody() {
RestAssured.with()
.formParam("name", "Quarkus")
.post("/hello")
.then().body(Matchers.equalTo("hello Quarkus"));
}

@Test
public void testMethodWithUndeclaredBody() {
RestAssured.with()
.formParam("name", "Quarkus")
.post("/hello/empty")
.then().body(Matchers.equalTo("hello Quarkus"));
}

@Path("hello")
public static class HelloResource {

@POST
public String helloPost(@RestForm String name) {
return "hello " + name;
}

@WithFormRead
@Path("empty")
@POST
public String helloEmptyPost(ServerRequestContext requestContext) {
return "hello " + ((ResteasyReactiveRequestContext) requestContext).getFormParameter("name", true, false);
}
}
}
Expand Up @@ -570,7 +570,8 @@ private ResourceMethod createResourceMethod(ClassInfo currentClassInfo, ClassInf
basicResourceClassInfo.getConsumes());
boolean suspended = false;
boolean sse = false;
boolean formParamRequired = false;
boolean formParamRequired = getAnnotationStore().getAnnotation(currentMethodInfo,
ResteasyReactiveDotNames.WITH_FORM_READ) != null;
Set<String> fileFormNames = new HashSet<>();
Type bodyParamType = null;
TypeArgMapper typeArgMapper = new TypeArgMapper(currentMethodInfo.declaringClass(), index);
Expand Down
Expand Up @@ -261,6 +261,9 @@ public final class ResteasyReactiveDotNames {
public static final DotName RESTEASY_REACTIVE_CONTAINER_REQUEST_CONTEXT = DotName
.createSimple("org.jboss.resteasy.reactive.server.spi.ResteasyReactiveContainerRequestContext");

public static final DotName WITH_FORM_READ = DotName
.createSimple("org.jboss.resteasy.reactive.server.WithFormRead");

public static final DotName OBJECT = DotName.createSimple(Object.class.getName());

public static final DotName CONTINUATION = DotName.createSimple("kotlin.coroutines.Continuation");
Expand Down
Expand Up @@ -16,7 +16,7 @@ public class ResourceInterceptor<T>
private BeanFactory<T> factory;
private int priority = Priorities.USER; // default priority as defined by spec
private boolean nonBlockingRequired; // whether or not @NonBlocking was specified on the class
private boolean readBody; // whether or not 'readBody' was set true for this filter
private boolean withFormRead; // whether or not '@WithFormRead' was set on this filter

/**
* The class names of the {@code @NameBinding} annotations that the method is annotated with.
Expand Down Expand Up @@ -76,12 +76,12 @@ public void setNonBlockingRequired(boolean nonBlockingRequired) {
this.nonBlockingRequired = nonBlockingRequired;
}

public boolean isReadBody() {
return readBody;
public boolean isWithFormRead() {
return withFormRead;
}

public void setReadBody(boolean readBody) {
this.readBody = readBody;
public void setWithFormRead(boolean withFormRead) {
this.withFormRead = withFormRead;
}

// spec says that writer interceptors are sorted in ascending order
Expand Down

0 comments on commit f256c11

Please sign in to comment.