Move response status processing in InvocableHandlerMethod
Prior to this commit, WebFlux would look at the handler method annotations (`@ResponseStatus`) for each handler execution, even calling the expensive `synthesizeAnnotation`. This commit moves this logic to the InvocableHandlerMethod so that this executed once at instantiation time and for all result handlers. Issue: SPR-15227
This commit is contained in:
@@ -24,8 +24,10 @@ import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.reactive.BindingContext;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.reactive.result.ResolvableMethod;
|
||||
@@ -147,6 +149,15 @@ public class InvocableHandlerMethodTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeMethodWithResponseStatus() throws Exception {
|
||||
InvocableHandlerMethod hm = handlerMethod("responseStatus");
|
||||
Mono<HandlerResult> mono = hm.invoke(this.exchange, new BindingContext());
|
||||
|
||||
assertHandlerResultValue(mono, "created");
|
||||
assertThat(this.exchange.getResponse().getStatusCode(), is(HttpStatus.CREATED));
|
||||
}
|
||||
|
||||
|
||||
private InvocableHandlerMethod handlerMethod(String name) throws Exception {
|
||||
TestController controller = new TestController();
|
||||
@@ -186,6 +197,11 @@ public class InvocableHandlerMethodTests {
|
||||
public void exceptionMethod() {
|
||||
throw new IllegalStateException("boo");
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
public String responseStatus() {
|
||||
return "created";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,13 +23,11 @@ import java.util.List;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import rx.Completable;
|
||||
import rx.Single;
|
||||
|
||||
import org.springframework.core.codec.ByteBufferEncoder;
|
||||
import org.springframework.core.codec.CharSequenceEncoder;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
@@ -42,7 +40,6 @@ import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
@@ -119,24 +116,6 @@ public class ResponseBodyResultHandlerTests {
|
||||
testSupports(controller, "handleToMonoResponseEntity", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeResponseStatus() throws NoSuchMethodException {
|
||||
Object controller = new TestRestController();
|
||||
HandlerMethod hm = handlerMethod(controller, "handleToString");
|
||||
HandlerResult handlerResult = new HandlerResult(hm, null, hm.getReturnType());
|
||||
|
||||
initExchange();
|
||||
StepVerifier.create(this.resultHandler.handleResult(this.exchange, handlerResult)).expectComplete().verify();
|
||||
assertEquals(HttpStatus.NO_CONTENT, this.response.getStatusCode());
|
||||
|
||||
hm = handlerMethod(controller, "handleToMonoVoid");
|
||||
handlerResult = new HandlerResult(hm, null, hm.getReturnType());
|
||||
|
||||
initExchange();
|
||||
StepVerifier.create(this.resultHandler.handleResult(this.exchange, handlerResult)).expectComplete().verify();
|
||||
assertEquals(HttpStatus.CREATED, this.response.getStatusCode());
|
||||
}
|
||||
|
||||
private void testSupports(Object controller, String method, boolean result) throws NoSuchMethodException {
|
||||
HandlerMethod hm = handlerMethod(controller, method);
|
||||
HandlerResult handlerResult = new HandlerResult(hm, null, hm.getReturnType());
|
||||
@@ -157,10 +136,8 @@ public class ResponseBodyResultHandlerTests {
|
||||
@RestController @SuppressWarnings("unused")
|
||||
private static class TestRestController {
|
||||
|
||||
@ResponseStatus(code = HttpStatus.CREATED)
|
||||
public Mono<Void> handleToMonoVoid() { return null;}
|
||||
|
||||
@ResponseStatus(code = HttpStatus.NO_CONTENT)
|
||||
public String handleToString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ import org.springframework.core.Ordered;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
@@ -49,7 +48,6 @@ import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse
|
||||
import org.springframework.ui.ConcurrentModel;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.reactive.BindingContext;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.reactive.accept.HeaderContentTypeResolver;
|
||||
@@ -131,23 +129,19 @@ public class ViewResolutionResultHandlerTests {
|
||||
|
||||
returnType = forClass(View.class);
|
||||
returnValue = new TestView("account");
|
||||
ServerWebExchange exchange = testHandle("/path", returnType, returnValue, "account: {id=123}");
|
||||
assertEquals(HttpStatus.NO_CONTENT, exchange.getResponse().getStatusCode());
|
||||
testHandle("/path", returnType, returnValue, "account: {id=123}");
|
||||
|
||||
returnType = forClassWithGenerics(Mono.class, View.class);
|
||||
returnValue = Mono.just(new TestView("account"));
|
||||
exchange = testHandle("/path", returnType, returnValue, "account: {id=123}");
|
||||
assertEquals(HttpStatus.SEE_OTHER, exchange.getResponse().getStatusCode());
|
||||
testHandle("/path", returnType, returnValue, "account: {id=123}");
|
||||
|
||||
returnType = forClass(String.class);
|
||||
returnValue = "account";
|
||||
exchange = testHandle("/path", returnType, returnValue, "account: {id=123}", resolver);
|
||||
assertEquals(HttpStatus.CREATED, exchange.getResponse().getStatusCode());
|
||||
testHandle("/path", returnType, returnValue, "account: {id=123}", resolver);
|
||||
|
||||
returnType = forClassWithGenerics(Mono.class, String.class);
|
||||
returnValue = Mono.just("account");
|
||||
exchange = testHandle("/path", returnType, returnValue, "account: {id=123}", resolver);
|
||||
assertEquals(HttpStatus.PARTIAL_CONTENT, exchange.getResponse().getStatusCode());
|
||||
testHandle("/path", returnType, returnValue, "account: {id=123}", resolver);
|
||||
|
||||
returnType = forClass(Model.class);
|
||||
returnValue = new ConcurrentModel().addAttribute("name", "Joe");
|
||||
@@ -438,38 +432,61 @@ public class ViewResolutionResultHandlerTests {
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestController {
|
||||
|
||||
@ResponseStatus(code = HttpStatus.CREATED)
|
||||
String string() { return null; }
|
||||
String string() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
View view() { return null; }
|
||||
View view() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.PARTIAL_CONTENT)
|
||||
Mono<String> monoString() { return null; }
|
||||
Mono<String> monoString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ResponseStatus(code = HttpStatus.SEE_OTHER)
|
||||
Mono<View> monoView() { return null; }
|
||||
Mono<View> monoView() {
|
||||
return null;
|
||||
}
|
||||
|
||||
Mono<Void> monoVoid() { return null; }
|
||||
Mono<Void> monoVoid() {
|
||||
return null;
|
||||
}
|
||||
|
||||
void voidMethod() {}
|
||||
void voidMethod() {
|
||||
}
|
||||
|
||||
Single<String> singleString() { return null; }
|
||||
Single<String> singleString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
Single<View> singleView() { return null; }
|
||||
Single<View> singleView() {
|
||||
return null;
|
||||
}
|
||||
|
||||
Completable completable() { return null; }
|
||||
Completable completable() {
|
||||
return null;
|
||||
}
|
||||
|
||||
Model model() { return null; }
|
||||
Model model() {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map map() { return null; }
|
||||
Map map() {
|
||||
return null;
|
||||
}
|
||||
|
||||
TestBean testBean() { return null; }
|
||||
TestBean testBean() {
|
||||
return null;
|
||||
}
|
||||
|
||||
Integer integer() { return null; }
|
||||
Integer integer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ModelAttribute("num")
|
||||
Long longAttribute() { return null; }
|
||||
Long longAttribute() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user