mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Add basic support for @RequestMapping
Just enough for a test with an @ResponseBody method that accepts an @RequestParam String arg and returning Publisher<String> or String. See RequestMappingIntegrationTests.
This commit is contained in:
+35
-8
@@ -17,12 +17,18 @@ package org.springframework.reactive.web.dispatch;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.rx.Streams;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.reactive.web.http.HttpHandler;
|
||||
import org.springframework.reactive.web.http.ServerHttpRequest;
|
||||
@@ -31,7 +37,10 @@ import org.springframework.reactive.web.http.ServerHttpResponse;
|
||||
/**
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class DispatcherHandler implements HttpHandler {
|
||||
public class DispatcherHandler implements HttpHandler, ApplicationContextAware {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DispatcherHandler.class);
|
||||
|
||||
|
||||
private List<HandlerMapping> handlerMappings;
|
||||
|
||||
@@ -40,22 +49,40 @@ public class DispatcherHandler implements HttpHandler {
|
||||
private List<HandlerResultHandler> resultHandlers;
|
||||
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
initStrategies(applicationContext);
|
||||
}
|
||||
|
||||
protected void initStrategies(ApplicationContext context) {
|
||||
|
||||
this.handlerMappings = new ArrayList<>(BeanFactoryUtils.beansOfTypeIncludingAncestors(
|
||||
context, HandlerMapping.class, true, false).values());
|
||||
Map<String, HandlerMapping> mappingBeans =
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
|
||||
|
||||
this.handlerAdapters = new ArrayList<>(BeanFactoryUtils.beansOfTypeIncludingAncestors(
|
||||
context, HandlerAdapter.class, true, false).values());
|
||||
this.handlerMappings = new ArrayList<>(mappingBeans.values());
|
||||
AnnotationAwareOrderComparator.sort(this.handlerMappings);
|
||||
|
||||
this.resultHandlers = new ArrayList<>(BeanFactoryUtils.beansOfTypeIncludingAncestors(
|
||||
context, HandlerResultHandler.class, true, false).values());
|
||||
Map<String, HandlerAdapter> adapterBeans =
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
|
||||
|
||||
this.handlerAdapters = new ArrayList<>(adapterBeans.values());
|
||||
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
|
||||
|
||||
Map<String, HandlerResultHandler> beans =
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerResultHandler.class, true, false);
|
||||
|
||||
this.resultHandlers = new ArrayList<>(beans.values());
|
||||
AnnotationAwareOrderComparator.sort(this.resultHandlers);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Publisher<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Processing " + request.getMethod() + " request for [" + request.getURI() + "]");
|
||||
}
|
||||
|
||||
Object handler = getHandler(request);
|
||||
if (handler == null) {
|
||||
// No exception handling mechanism yet
|
||||
@@ -73,7 +100,7 @@ public class DispatcherHandler implements HttpHandler {
|
||||
}
|
||||
}
|
||||
return Streams.fail(new IllegalStateException(
|
||||
"No HandlerResultHandler for " + result.getReturnValue()));
|
||||
"No HandlerResultHandler for " + result.getValue()));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+13
-5
@@ -15,21 +15,29 @@
|
||||
*/
|
||||
package org.springframework.reactive.web.dispatch;
|
||||
|
||||
|
||||
/**
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class HandlerResult {
|
||||
|
||||
private final Object returnValue;
|
||||
private final Object handler;
|
||||
|
||||
private final Object value;
|
||||
|
||||
|
||||
public HandlerResult(Object returnValue) {
|
||||
this.returnValue = returnValue;
|
||||
public HandlerResult(Object handler, Object value) {
|
||||
this.handler = handler;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
public Object getReturnValue() {
|
||||
return this.returnValue;
|
||||
public Object getHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.reactive.web.dispatch.method;
|
||||
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.reactive.web.http.ServerHttpRequest;
|
||||
|
||||
|
||||
/**
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public interface HandlerMethodArgumentResolver {
|
||||
|
||||
boolean supportsParameter(MethodParameter parameter);
|
||||
|
||||
Object resolveArgument(MethodParameter parameter, ServerHttpRequest request);
|
||||
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.reactive.web.dispatch.method;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.reactive.web.http.ServerHttpRequest;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
||||
|
||||
/**
|
||||
* 90% overlap with the existing one in spring-web except for the different
|
||||
* HandlerMethodArgumentResolver contract.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class InvocableHandlerMethod extends HandlerMethod {
|
||||
|
||||
private List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
|
||||
|
||||
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
|
||||
|
||||
|
||||
public InvocableHandlerMethod(HandlerMethod handlerMethod) {
|
||||
super(handlerMethod);
|
||||
}
|
||||
|
||||
|
||||
public void setHandlerMethodArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
|
||||
this.argumentResolvers.clear();
|
||||
this.argumentResolvers.addAll(resolvers);
|
||||
}
|
||||
|
||||
|
||||
public Object invokeForRequest(ServerHttpRequest request, Object... providedArgs) throws Exception {
|
||||
Object[] args = getMethodArgumentValues(request, providedArgs);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Invoking [" + getBeanType().getSimpleName() + "." +
|
||||
getMethod().getName() + "] method with arguments " + Arrays.asList(args));
|
||||
}
|
||||
Object returnValue = doInvoke(args);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
private Object[] getMethodArgumentValues(ServerHttpRequest request, Object... providedArgs) throws Exception {
|
||||
MethodParameter[] parameters = getMethodParameters();
|
||||
Object[] args = new Object[parameters.length];
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
MethodParameter parameter = parameters[i];
|
||||
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
|
||||
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
|
||||
args[i] = resolveProvidedArgument(parameter, providedArgs);
|
||||
if (args[i] != null) {
|
||||
continue;
|
||||
}
|
||||
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
|
||||
if (resolver.supportsParameter(parameter)) {
|
||||
try {
|
||||
args[i] = resolver.resolveArgument(parameter, request);
|
||||
break;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (args[i] == null) {
|
||||
String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
private String getArgumentResolutionErrorMessage(String message, int index) {
|
||||
MethodParameter param = getMethodParameters()[index];
|
||||
message += " [" + index + "] [type=" + param.getParameterType().getName() + "]";
|
||||
return getDetailedErrorMessage(message);
|
||||
}
|
||||
|
||||
protected String getDetailedErrorMessage(String message) {
|
||||
return message + "\n" + "HandlerMethod details: \n" +
|
||||
"Controller [" + getBeanType().getName() + "]\n" +
|
||||
"Method [" + getBridgedMethod().toGenericString() + "]\n";
|
||||
}
|
||||
|
||||
private Object resolveProvidedArgument(MethodParameter parameter, Object... providedArgs) {
|
||||
if (providedArgs == null) {
|
||||
return null;
|
||||
}
|
||||
for (Object providedArg : providedArgs) {
|
||||
if (parameter.getParameterType().isInstance(providedArg)) {
|
||||
return providedArg;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Object doInvoke(Object... args) throws Exception {
|
||||
ReflectionUtils.makeAccessible(getBridgedMethod());
|
||||
try {
|
||||
return getBridgedMethod().invoke(getBean(), args);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
assertTargetBean(getBridgedMethod(), getBean(), args);
|
||||
throw new IllegalStateException(getInvocationErrorMessage(ex.getMessage(), args), ex);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
// Unwrap for HandlerExceptionResolvers ...
|
||||
Throwable targetException = ex.getTargetException();
|
||||
if (targetException instanceof RuntimeException) {
|
||||
throw (RuntimeException) targetException;
|
||||
}
|
||||
else if (targetException instanceof Error) {
|
||||
throw (Error) targetException;
|
||||
}
|
||||
else if (targetException instanceof Exception) {
|
||||
throw (Exception) targetException;
|
||||
}
|
||||
else {
|
||||
String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
|
||||
throw new IllegalStateException(msg, targetException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertTargetBean(Method method, Object targetBean, Object[] args) {
|
||||
Class<?> methodDeclaringClass = method.getDeclaringClass();
|
||||
Class<?> targetBeanClass = targetBean.getClass();
|
||||
if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) {
|
||||
String msg = "The mapped controller method class '" + methodDeclaringClass.getName() +
|
||||
"' is not an instance of the actual controller bean instance '" +
|
||||
targetBeanClass.getName() + "'. If the controller requires proxying " +
|
||||
"(e.g. due to @Transactional), please use class-based proxying.";
|
||||
throw new IllegalStateException(getInvocationErrorMessage(msg, args));
|
||||
}
|
||||
}
|
||||
|
||||
private String getInvocationErrorMessage(String message, Object[] resolvedArgs) {
|
||||
StringBuilder sb = new StringBuilder(getDetailedErrorMessage(message));
|
||||
sb.append("Resolved arguments: \n");
|
||||
for (int i=0; i < resolvedArgs.length; i++) {
|
||||
sb.append("[").append(i).append("] ");
|
||||
if (resolvedArgs[i] == null) {
|
||||
sb.append("[null] \n");
|
||||
}
|
||||
else {
|
||||
sb.append("[type=").append(resolvedArgs[i].getClass().getName()).append("] ");
|
||||
sb.append("[value=").append(resolvedArgs[i]).append("]\n");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.reactive.web.dispatch.method.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.rx.Streams;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.reactive.web.dispatch.HandlerAdapter;
|
||||
import org.springframework.reactive.web.dispatch.HandlerResult;
|
||||
import org.springframework.reactive.web.dispatch.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.reactive.web.dispatch.method.InvocableHandlerMethod;
|
||||
import org.springframework.reactive.web.http.ServerHttpRequest;
|
||||
import org.springframework.reactive.web.http.ServerHttpResponse;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class RequestMappingHandlerAdapter implements HandlerAdapter, InitializingBean {
|
||||
|
||||
private List<HandlerMethodArgumentResolver> argumentResolvers;
|
||||
|
||||
|
||||
public void setHandlerMethodArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
|
||||
this.argumentResolvers.clear();
|
||||
this.argumentResolvers.addAll(resolvers);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (this.argumentResolvers == null) {
|
||||
this.argumentResolvers = new ArrayList<>();
|
||||
this.argumentResolvers.add(new RequestParamArgumentResolver());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Object handler) {
|
||||
return HandlerMethod.class.equals(handler.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Publisher<HandlerResult> handle(ServerHttpRequest request, ServerHttpResponse response,
|
||||
Object handler) {
|
||||
|
||||
final InvocableHandlerMethod invocable = new InvocableHandlerMethod((HandlerMethod) handler);
|
||||
invocable.setHandlerMethodArgumentResolvers(this.argumentResolvers);
|
||||
|
||||
Object result;
|
||||
try {
|
||||
result = invocable.invokeForRequest(request);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return Streams.fail(ex);
|
||||
}
|
||||
|
||||
return Streams.just(new HandlerResult(invocable, result));
|
||||
}
|
||||
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.reactive.web.dispatch.method.annotation;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.reactive.web.dispatch.HandlerMapping;
|
||||
import org.springframework.reactive.web.http.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.HandlerMethodSelector;
|
||||
|
||||
|
||||
/**
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class RequestMappingHandlerMapping implements HandlerMapping,
|
||||
ApplicationContextAware, InitializingBean {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(RequestMappingHandlerMapping.class);
|
||||
|
||||
|
||||
private final Map<String, HandlerMethod> methodMap = new LinkedHashMap<>();
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
for (Object bean : this.applicationContext.getBeansOfType(Object.class).values()) {
|
||||
detectHandlerMethods(bean);
|
||||
}
|
||||
}
|
||||
|
||||
protected void detectHandlerMethods(final Object bean) {
|
||||
final Class<?> beanType = bean.getClass();
|
||||
if (AnnotationUtils.findAnnotation(beanType, Controller.class) != null) {
|
||||
HandlerMethodSelector.selectMethods(beanType, method -> {
|
||||
RequestMapping annotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
|
||||
if (annotation != null && annotation.value().length > 0) {
|
||||
String path = annotation.value()[0];
|
||||
HandlerMethod handlerMethod = new HandlerMethod(bean, method);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Mapped \"" + path + "\" onto " + handlerMethod);
|
||||
}
|
||||
methodMap.put(path, handlerMethod);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getHandler(ServerHttpRequest request) {
|
||||
String path = request.getURI().getPath();
|
||||
HandlerMethod handlerMethod = this.methodMap.get(path);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Mapped " + path + " to [" + handlerMethod + "]");
|
||||
}
|
||||
return handlerMethod;
|
||||
}
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.reactive.web.dispatch.method.annotation;
|
||||
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.reactive.web.dispatch.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.reactive.web.http.ServerHttpRequest;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
|
||||
/**
|
||||
* Support {@code @RequestParam} but for query params only.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class RequestParamArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(RequestParam.class);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter param, ServerHttpRequest request) {
|
||||
RequestParam annotation = param.getParameterAnnotation(RequestParam.class);
|
||||
String name = (annotation.value().length() != 0 ? annotation.value() : param.getParameterName());
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUri(request.getURI()).build();
|
||||
return uriComponents.getQueryParams().getFirst(name);
|
||||
}
|
||||
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.reactive.web.dispatch.method.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.rx.Streams;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.reactive.web.dispatch.HandlerResult;
|
||||
import org.springframework.reactive.web.dispatch.HandlerResultHandler;
|
||||
import org.springframework.reactive.web.http.ServerHttpRequest;
|
||||
import org.springframework.reactive.web.http.ServerHttpResponse;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
||||
|
||||
/**
|
||||
* For now a simple {@code String} or {@code Publisher<String>} to
|
||||
* "text/plain;charset=UTF-8" conversion.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered {
|
||||
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supports(HandlerResult result) {
|
||||
Object handler = result.getHandler();
|
||||
if (handler instanceof HandlerMethod) {
|
||||
Method method = ((HandlerMethod) handler).getMethod();
|
||||
return AnnotatedElementUtils.isAnnotated(method, ResponseBody.class.getName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Publisher<Void> handleResult(ServerHttpRequest request, ServerHttpResponse response,
|
||||
HandlerResult result) {
|
||||
|
||||
Object value = result.getValue();
|
||||
HandlerMethod handlerMethod = (HandlerMethod) result.getHandler();
|
||||
MethodParameter returnType = handlerMethod.getReturnValueType(value);
|
||||
|
||||
if (value == null) {
|
||||
return Streams.empty();
|
||||
}
|
||||
|
||||
if (value instanceof String) {
|
||||
response.getHeaders().setContentType(new MediaType("text", "plain", UTF_8));
|
||||
return response.writeWith(Streams.just(((String) value).getBytes(UTF_8)));
|
||||
}
|
||||
else if (value instanceof Publisher) {
|
||||
Class<?> type = ResolvableType.forMethodParameter(returnType).resolveGeneric(0);
|
||||
if (String.class.equals(type)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Publisher<String> content = (Publisher<String>) value;
|
||||
return response.writeWith(Streams.wrap(content).map(value1 -> value1.getBytes(UTF_8)));
|
||||
}
|
||||
}
|
||||
|
||||
return Streams.fail(new IllegalStateException("Return value type not supported: " + returnType));
|
||||
}
|
||||
|
||||
}
|
||||
+3
-1
@@ -65,7 +65,9 @@ public class RxNettyServerHttpResponse implements ServerHttpResponse {
|
||||
private void writeHeaders() {
|
||||
if (!this.headersWritten) {
|
||||
for (String name : this.headers.keySet()) {
|
||||
this.response.setHeader(name, this.headers.get(name));
|
||||
for (String value : this.headers.get(name)) {
|
||||
this.response.addHeader(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-2
@@ -29,6 +29,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.reactive.web.http.HttpHandler;
|
||||
|
||||
/**
|
||||
@@ -66,7 +67,7 @@ public class HttpHandlerServlet extends HttpServlet {
|
||||
response.getOutputStream().setWriteListener(responseSubscriber);
|
||||
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response, responseSubscriber);
|
||||
|
||||
HandlerResultSubscriber resultSubscriber = new HandlerResultSubscriber(contextSynchronizer);
|
||||
HandlerResultSubscriber resultSubscriber = new HandlerResultSubscriber(contextSynchronizer, httpResponse);
|
||||
this.handler.handle(httpRequest, httpResponse).subscribe(resultSubscriber);
|
||||
}
|
||||
|
||||
@@ -75,9 +76,12 @@ public class HttpHandlerServlet extends HttpServlet {
|
||||
|
||||
private final AsyncContextSynchronizer synchronizer;
|
||||
|
||||
private final ServletServerHttpResponse response;
|
||||
|
||||
public HandlerResultSubscriber(AsyncContextSynchronizer synchronizer) {
|
||||
|
||||
public HandlerResultSubscriber(AsyncContextSynchronizer synchronizer, ServletServerHttpResponse response) {
|
||||
this.synchronizer = synchronizer;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +98,7 @@ public class HttpHandlerServlet extends HttpServlet {
|
||||
@Override
|
||||
public void onError(Throwable ex) {
|
||||
logger.error("Error from request handling. Completing the request.", ex);
|
||||
this.response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
this.synchronizer.complete();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user