下面列出了org.springframework.http.server.reactive.AbstractServerHttpResponse#org.springframework.web.reactive.HandlerResult 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Override
public boolean supports(HandlerResult result) {
if (hasModelAnnotation(result.getReturnTypeSource())) {
return true;
}
Class<?> type = result.getReturnType().toClass();
ReactiveAdapter adapter = getAdapter(result);
if (adapter != null) {
if (adapter.isNoValue()) {
return true;
}
type = result.getReturnType().getGeneric().toClass();
}
return (CharSequence.class.isAssignableFrom(type) || Rendering.class.isAssignableFrom(type) ||
Model.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type) ||
void.class.equals(type) || View.class.isAssignableFrom(type) ||
!BeanUtils.isSimpleProperty(type));
}
@Test
public void handleReturnValueETagAndLastModified() throws Exception {
String eTag = "\"deadb33f8badf00d\"";
Instant currentTime = Instant.now().truncatedTo(ChronoUnit.SECONDS);
Instant oneMinAgo = currentTime.minusSeconds(60);
MockServerWebExchange exchange = MockServerWebExchange.from(get("/path")
.ifNoneMatch(eTag)
.ifModifiedSince(currentTime.toEpochMilli())
);
ResponseEntity<String> entity = ok().eTag(eTag).lastModified(oneMinAgo.toEpochMilli()).body("body");
MethodParameter returnType = on(TestController.class).resolveReturnType(entity(String.class));
HandlerResult result = handlerResult(entity, returnType);
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
assertConditionalResponse(exchange, HttpStatus.NOT_MODIFIED, null, eTag, oneMinAgo);
}
/**
* Invoke the method for the given exchange.
* @param exchange the current exchange
* @param bindingContext the binding context to use
* @param providedArgs optional list of argument values to match by type
* @return a Mono with a {@link HandlerResult}.
* @throws ServerErrorException if method argument resolution or method invocation fails
*/
@Nullable
public HandlerResult invokeForHandlerResult(ServerWebExchange exchange,
BindingContext bindingContext, Object... providedArgs) {
MonoProcessor<HandlerResult> processor = MonoProcessor.create();
this.delegate.invoke(exchange, bindingContext, providedArgs).subscribeWith(processor);
if (processor.isTerminated()) {
Throwable ex = processor.getError();
if (ex != null) {
throw (ex instanceof ServerErrorException ? (ServerErrorException) ex :
new ServerErrorException("Failed to invoke: " + getShortLogMessage(), getMethod(), ex));
}
return processor.peek();
}
else {
// Should never happen...
throw new IllegalStateException(
"SyncInvocableHandlerMethod should have completed synchronously.");
}
}
private void testDefaultViewName(Object returnValue, MethodParameter returnType) {
this.bindingContext.getModel().addAttribute("id", "123");
HandlerResult result = new HandlerResult(new Object(), returnValue, returnType, this.bindingContext);
ViewResolutionResultHandler handler = resultHandler(new TestViewResolver("account"));
MockServerWebExchange exchange = MockServerWebExchange.from(get("/account"));
handler.handleResult(exchange, result).block(Duration.ofMillis(5000));
assertResponseBody(exchange, "account: {id=123}");
exchange = MockServerWebExchange.from(get("/account/"));
handler.handleResult(exchange, result).block(Duration.ofMillis(5000));
assertResponseBody(exchange, "account: {id=123}");
exchange = MockServerWebExchange.from(get("/account.123"));
handler.handleResult(exchange, result).block(Duration.ofMillis(5000));
assertResponseBody(exchange, "account: {id=123}");
}
@Test
public void contentNegotiation() {
TestBean value = new TestBean("Joe");
MethodParameter returnType = on(Handler.class).resolveReturnType(TestBean.class);
HandlerResult handlerResult = new HandlerResult(new Object(), value, returnType, this.bindingContext);
MockServerWebExchange exchange = MockServerWebExchange.from(get("/account").accept(APPLICATION_JSON));
TestView defaultView = new TestView("jsonView", APPLICATION_JSON);
resultHandler(Collections.singletonList(defaultView), new TestViewResolver("account"))
.handleResult(exchange, handlerResult)
.block(Duration.ofSeconds(5));
assertEquals(APPLICATION_JSON, exchange.getResponse().getHeaders().getContentType());
assertResponseBody(exchange, "jsonView: {" +
"org.springframework.validation.BindingResult.testBean=" +
"org.springframework.validation.BeanPropertyBindingResult: 0 errors, " +
"testBean=TestBean[name=Joe]" +
"}");
}
@Test // SPR-15291
public void contentNegotiationWithRedirect() {
HandlerResult handlerResult = new HandlerResult(new Object(), "redirect:/",
on(Handler.class).annotNotPresent(ModelAttribute.class).resolveReturnType(String.class),
this.bindingContext);
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
viewResolver.setApplicationContext(new StaticApplicationContext());
ViewResolutionResultHandler resultHandler = resultHandler(viewResolver);
MockServerWebExchange exchange = MockServerWebExchange.from(get("/account").accept(APPLICATION_JSON));
resultHandler.handleResult(exchange, handlerResult).block(Duration.ZERO);
MockServerHttpResponse response = exchange.getResponse();
assertEquals(303, response.getStatusCode().value());
assertEquals("/", response.getHeaders().getLocation().toString());
}
@Test
public void handleReturnValueETagAndLastModified() throws Exception {
String eTag = "\"deadb33f8badf00d\"";
Instant currentTime = Instant.now().truncatedTo(ChronoUnit.SECONDS);
Instant oneMinAgo = currentTime.minusSeconds(60);
MockServerWebExchange exchange = MockServerWebExchange.from(get("/path")
.ifNoneMatch(eTag)
.ifModifiedSince(currentTime.toEpochMilli())
);
ResponseEntity<String> entity = ok().eTag(eTag).lastModified(oneMinAgo.toEpochMilli()).body("body");
MethodParameter returnType = on(TestController.class).resolveReturnType(entity(String.class));
HandlerResult result = handlerResult(entity, returnType);
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
assertConditionalResponse(exchange, HttpStatus.NOT_MODIFIED, null, eTag, oneMinAgo);
}
@Test
public void handleReturnValueChangedETagAndLastModified() throws Exception {
String etag = "\"deadb33f8badf00d\"";
String newEtag = "\"changed-etag-value\"";
Instant currentTime = Instant.now().truncatedTo(ChronoUnit.SECONDS);
Instant oneMinAgo = currentTime.minusSeconds(60);
MockServerWebExchange exchange = MockServerWebExchange.from(get("/path")
.ifNoneMatch(etag)
.ifModifiedSince(currentTime.toEpochMilli())
);
ResponseEntity<String> entity = ok().eTag(newEtag).lastModified(oneMinAgo.toEpochMilli()).body("body");
MethodParameter returnType = on(TestController.class).resolveReturnType(entity(String.class));
HandlerResult result = handlerResult(entity, returnType);
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
assertConditionalResponse(exchange, HttpStatus.OK, "body", newEtag, oneMinAgo);
}
private void testDefaultViewName(Object returnValue, MethodParameter returnType) {
this.bindingContext.getModel().addAttribute("id", "123");
HandlerResult result = new HandlerResult(new Object(), returnValue, returnType, this.bindingContext);
ViewResolutionResultHandler handler = resultHandler(new TestViewResolver("account"));
MockServerWebExchange exchange = MockServerWebExchange.from(get("/account"));
handler.handleResult(exchange, result).block(Duration.ofMillis(5000));
assertResponseBody(exchange, "account: {id=123}");
exchange = MockServerWebExchange.from(get("/account/"));
handler.handleResult(exchange, result).block(Duration.ofMillis(5000));
assertResponseBody(exchange, "account: {id=123}");
exchange = MockServerWebExchange.from(get("/account.123"));
handler.handleResult(exchange, result).block(Duration.ofMillis(5000));
assertResponseBody(exchange, "account: {id=123}");
}
private void testHttpOptions(String requestURI, Set<HttpMethod> allowedMethods) {
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.options(requestURI));
HandlerMethod handlerMethod = (HandlerMethod) this.handlerMapping.getHandler(exchange).block();
BindingContext bindingContext = new BindingContext();
InvocableHandlerMethod invocable = new InvocableHandlerMethod(handlerMethod);
Mono<HandlerResult> mono = invocable.invoke(exchange, bindingContext);
HandlerResult result = mono.block();
assertNotNull(result);
Object value = result.getReturnValue();
assertNotNull(value);
assertEquals(HttpHeaders.class, value.getClass());
assertEquals(allowedMethods, ((HttpHeaders) value).getAllow());
}
@Test
public void illegalArgumentException() {
this.resolvers.add(stubResolver(1));
Method method = ResolvableMethod.on(TestController.class).mockCall(o -> o.singleArg(null)).method();
Mono<HandlerResult> mono = invoke(new TestController(), method);
try {
mono.block();
fail("Expected IllegalStateException");
}
catch (IllegalStateException ex) {
assertNotNull("Exception not wrapped", ex.getCause());
assertTrue(ex.getCause() instanceof IllegalArgumentException);
assertTrue(ex.getMessage().contains("Controller ["));
assertTrue(ex.getMessage().contains("Method ["));
assertTrue(ex.getMessage().contains("with argument values:"));
assertTrue(ex.getMessage().contains("[0] [type=java.lang.Integer] [value=1]"));
}
}
@Test
public void handleReturnValueChangedETagAndLastModified() throws Exception {
String etag = "\"deadb33f8badf00d\"";
String newEtag = "\"changed-etag-value\"";
Instant currentTime = Instant.now().truncatedTo(ChronoUnit.SECONDS);
Instant oneMinAgo = currentTime.minusSeconds(60);
MockServerWebExchange exchange = MockServerWebExchange.from(get("/path")
.ifNoneMatch(etag)
.ifModifiedSince(currentTime.toEpochMilli())
);
ResponseEntity<String> entity = ok().eTag(newEtag).lastModified(oneMinAgo.toEpochMilli()).body("body");
MethodParameter returnType = on(TestController.class).resolveReturnType(entity(String.class));
HandlerResult result = handlerResult(entity, returnType);
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
assertConditionalResponse(exchange, HttpStatus.OK, "body", newEtag, oneMinAgo);
}
@Test // SPR-15291
public void contentNegotiationWithRedirect() {
HandlerResult handlerResult = new HandlerResult(new Object(), "redirect:/",
on(Handler.class).annotNotPresent(ModelAttribute.class).resolveReturnType(String.class),
this.bindingContext);
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
viewResolver.setApplicationContext(new StaticApplicationContext());
ViewResolutionResultHandler resultHandler = resultHandler(viewResolver);
MockServerWebExchange exchange = MockServerWebExchange.from(get("/account").accept(APPLICATION_JSON));
resultHandler.handleResult(exchange, handlerResult).block(Duration.ZERO);
MockServerHttpResponse response = exchange.getResponse();
assertEquals(303, response.getStatusCode().value());
assertEquals("/", response.getHeaders().getLocation().toString());
}
@Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;
ServerRequest request = exchange.getRequiredAttribute(RouterFunctions.REQUEST_ATTRIBUTE);
return handlerFunction.handle(request)
.map(response -> new HandlerResult(handlerFunction, response, HANDLER_FUNCTION_RETURN_TYPE));
}
@Test
public void exceptionInResolvingArg() {
this.resolvers.add(stubResolver(Mono.error(new UnsupportedMediaTypeStatusException("boo"))));
Method method = ResolvableMethod.on(TestController.class).mockCall(o -> o.singleArg(null)).method();
Mono<HandlerResult> mono = invoke(new TestController(), method);
try {
mono.block();
fail("Expected UnsupportedMediaTypeStatusException");
}
catch (UnsupportedMediaTypeStatusException ex) {
assertThat(ex.getMessage(), is("415 UNSUPPORTED_MEDIA_TYPE \"boo\""));
}
}
@Test
public void voidMonoMethodWithExchangeArg() {
this.resolvers.add(stubResolver(this.exchange));
Method method = ResolvableMethod.on(TestController.class).mockCall(c -> c.exchangeMonoVoid(exchange)).method();
HandlerResult result = invokeForResult(new TestController(), method);
assertNull("Expected no result (i.e. fully handled)", result);
assertEquals("body", this.exchange.getResponse().getBodyAsString().block(Duration.ZERO));
}
@Test
public void unresolvedViewName() {
String returnValue = "account";
MethodParameter returnType = on(Handler.class).annotPresent(ModelAttribute.class).resolveReturnType(String.class);
HandlerResult result = new HandlerResult(new Object(), returnValue, returnType, this.bindingContext);
MockServerWebExchange exchange = MockServerWebExchange.from(get("/path"));
Mono<Void> mono = resultHandler().handleResult(exchange, result);
StepVerifier.create(mono)
.expectNextCount(0)
.expectErrorMessage("Could not resolve view with name 'path'.")
.verify();
}
private Mono<Void> invokeModelAttributeMethods(BindingContext bindingContext,
List<InvocableHandlerMethod> modelMethods, ServerWebExchange exchange) {
List<Mono<HandlerResult>> resultList = new ArrayList<>();
modelMethods.forEach(invocable -> resultList.add(invocable.invoke(exchange, bindingContext)));
return Mono
.zip(resultList, objectArray ->
Arrays.stream(objectArray)
.map(object -> handleResult(((HandlerResult) object), bindingContext))
.collect(Collectors.toList()))
.flatMap(Mono::when);
}
private void invokeBinderMethod(
WebExchangeDataBinder dataBinder, ServerWebExchange exchange, SyncInvocableHandlerMethod binderMethod) {
HandlerResult result = binderMethod.invokeForHandlerResult(exchange, this.binderMethodContext, dataBinder);
if (result != null && result.getReturnValue() != null) {
throw new IllegalStateException(
"@InitBinder methods must not return a value (should be void): " + binderMethod);
}
// Should not happen (no Model argument resolution) ...
if (!this.binderMethodContext.getModel().asMap().isEmpty()) {
throw new IllegalStateException(
"@InitBinder methods are not allowed to add model attributes: " + binderMethod);
}
}
@Test
public void handleResponseEntityWithNullBody() throws Exception {
Object returnValue = Mono.just(notFound().build());
MethodParameter type = on(TestController.class).resolveReturnType(Mono.class, entity(String.class));
HandlerResult result = handlerResult(returnValue, type);
MockServerWebExchange exchange = MockServerWebExchange.from(get("/path"));
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
assertEquals(HttpStatus.NOT_FOUND, exchange.getResponse().getStatusCode());
assertResponseBodyIsEmpty(exchange);
}
@Override
public boolean supports(HandlerResult result) {
MethodParameter returnType = result.getReturnTypeSource();
Class<?> containingClass = returnType.getContainingClass();
return (AnnotatedElementUtils.hasAnnotation(containingClass, ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
private Mono<HandlerResult> handleException(Throwable exception, HandlerMethod handlerMethod,
BindingContext bindingContext, ServerWebExchange exchange) {
Assert.state(this.methodResolver != null, "Not initialized");
// Success and error responses may use different content types
exchange.getAttributes().remove(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
if (!exchange.getResponse().isCommitted()) {
exchange.getResponse().getHeaders().remove(HttpHeaders.CONTENT_TYPE);
}
InvocableHandlerMethod invocable = this.methodResolver.getExceptionHandlerMethod(exception, handlerMethod);
if (invocable != null) {
try {
if (logger.isDebugEnabled()) {
logger.debug(exchange.getLogPrefix() + "Using @ExceptionHandler " + invocable);
}
bindingContext.getModel().asMap().clear();
Throwable cause = exception.getCause();
if (cause != null) {
return invocable.invoke(exchange, bindingContext, exception, cause, handlerMethod);
}
else {
return invocable.invoke(exchange, bindingContext, exception, handlerMethod);
}
}
catch (Throwable invocationEx) {
if (logger.isWarnEnabled()) {
logger.warn(exchange.getLogPrefix() + "Failure in @ExceptionHandler " + invocable, invocationEx);
}
}
}
return Mono.error(exception);
}
@Test
public void checkNotModified() {
MockServerHttpRequest request = MockServerHttpRequest.get("/").ifModifiedSince(10 * 1000 * 1000).build();
ServerWebExchange exchange = MockServerWebExchange.from(request);
this.resolvers.add(stubResolver(exchange));
Method method = ResolvableMethod.on(TestController.class).mockCall(c -> c.notModified(exchange)).method();
HandlerResult result = invokeForResult(new TestController(), method);
assertNull("Expected no result (i.e. fully handled)", result);
}
@Test // SPR-17082
public void handleResponseEntityWithExistingResponseHeaders() throws Exception {
ResponseEntity<Void> value = ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).build();
MethodParameter returnType = on(TestController.class).resolveReturnType(entity(Void.class));
HandlerResult result = handlerResult(value, returnType);
MockServerWebExchange exchange = MockServerWebExchange.from(get("/path"));
exchange.getResponse().getHeaders().setContentType(MediaType.TEXT_PLAIN);
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
assertEquals(1, exchange.getResponse().getHeaders().size());
assertEquals(MediaType.APPLICATION_JSON, exchange.getResponse().getHeaders().getContentType());
assertResponseBodyIsEmpty(exchange);
}
private ServerWebExchange testHandle(String path, MethodParameter returnType, Object returnValue,
String responseBody, ViewResolver... resolvers) {
Model model = this.bindingContext.getModel();
model.asMap().clear();
model.addAttribute("id", "123");
HandlerResult result = new HandlerResult(new Object(), returnValue, returnType, this.bindingContext);
MockServerWebExchange exchange = MockServerWebExchange.from(get(path));
resultHandler(resolvers).handleResult(exchange, result).block(Duration.ofSeconds(5));
assertResponseBody(exchange, responseBody);
return exchange;
}
@Test
public void resolveProvidedArgFirst() {
this.resolvers.add(stubResolver("value1"));
Method method = ResolvableMethod.on(TestController.class).mockCall(o -> o.singleArg(null)).method();
Mono<HandlerResult> mono = invoke(new TestController(), method, "value2");
assertHandlerResultValue(mono, "success:value2");
}
@Test
public void httpHeaders() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAllow(new LinkedHashSet<>(Arrays.asList(HttpMethod.GET, HttpMethod.POST, HttpMethod.OPTIONS)));
MethodParameter returnType = on(TestController.class).resolveReturnType(entity(Void.class));
HandlerResult result = handlerResult(headers, returnType);
MockServerWebExchange exchange = MockServerWebExchange.from(get("/path"));
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
assertEquals(HttpStatus.OK, exchange.getResponse().getStatusCode());
assertEquals(1, exchange.getResponse().getHeaders().size());
assertEquals("GET,POST,OPTIONS", exchange.getResponse().getHeaders().getFirst("Allow"));
assertResponseBodyIsEmpty(exchange);
}
private HandlerResult handle(RequestMappingHandlerAdapter adapter,
Object controller, String methodName) throws Exception {
Method method = controller.getClass().getMethod(methodName);
HandlerMethod handlerMethod = new HandlerMethod(controller, method);
return adapter.handle(this.exchange, handlerMethod).block(Duration.ZERO);
}
@Test
public void handleResponseEntityWithNullBody() throws Exception {
Object returnValue = Mono.just(notFound().build());
MethodParameter type = on(TestController.class).resolveReturnType(Mono.class, entity(String.class));
HandlerResult result = handlerResult(returnValue, type);
MockServerWebExchange exchange = MockServerWebExchange.from(get("/path"));
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
assertEquals(HttpStatus.NOT_FOUND, exchange.getResponse().getStatusCode());
assertResponseBodyIsEmpty(exchange);
}
@Test
public void handleReturnValueEtag() throws Exception {
String etagValue = "\"deadb33f8badf00d\"";
MockServerWebExchange exchange = MockServerWebExchange.from(get("/path").ifNoneMatch(etagValue));
ResponseEntity<String> entity = ok().eTag(etagValue).body("body");
MethodParameter returnType = on(TestController.class).resolveReturnType(entity(String.class));
HandlerResult result = handlerResult(entity, returnType);
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
assertConditionalResponse(exchange, HttpStatus.NOT_MODIFIED, null, etagValue, Instant.MIN);
}