mirror of
https://gitee.com/mirrors/Spring-Cloud-Alibaba.git
synced 2021-06-26 13:25:11 +08:00
Polish spring-cloud-incubator/spring-cloud-alibaba#348 : @DubboTransported supports RestTemplate (part 2)
This commit is contained in:
parent
0b31eccbf0
commit
4b1c3c6454
@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.cloud.alibaba.dubbo.autoconfigure;
|
package org.springframework.cloud.alibaba.dubbo.autoconfigure;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -50,7 +51,7 @@ import java.util.Map;
|
|||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnClass(RestTemplate.class)
|
@ConditionalOnClass(RestTemplate.class)
|
||||||
@AutoConfigureAfter(LoadBalancerAutoConfiguration.class)
|
@AutoConfigureAfter(LoadBalancerAutoConfiguration.class)
|
||||||
public class DubboLoadBalancedRestTemplateAutoConfiguration {
|
public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClassLoaderAware {
|
||||||
|
|
||||||
private static final Class<DubboTransported> DUBBO_TRANSPORTED_CLASS = DubboTransported.class;
|
private static final Class<DubboTransported> DUBBO_TRANSPORTED_CLASS = DubboTransported.class;
|
||||||
|
|
||||||
@ -69,6 +70,8 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration {
|
|||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private Map<String, RestTemplate> restTemplates = Collections.emptyMap();
|
private Map<String, RestTemplate> restTemplates = Collections.emptyMap();
|
||||||
|
|
||||||
|
private ClassLoader classLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapt the {@link RestTemplate} beans that are annotated {@link LoadBalanced @LoadBalanced} and
|
* Adapt the {@link RestTemplate} beans that are annotated {@link LoadBalanced @LoadBalanced} and
|
||||||
* {@link LoadBalanced @LoadBalanced} when Spring Boot application started
|
* {@link LoadBalanced @LoadBalanced} when Spring Boot application started
|
||||||
@ -117,10 +120,14 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration {
|
|||||||
|
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
interceptors.set(index, new DubboAdapterLoadBalancerInterceptor(repository, loadBalancerInterceptor,
|
interceptors.set(index, new DubboAdapterLoadBalancerInterceptor(repository, loadBalancerInterceptor,
|
||||||
restTemplate.getMessageConverters()));
|
restTemplate.getMessageConverters(),classLoader));
|
||||||
}
|
}
|
||||||
|
|
||||||
restTemplate.setInterceptors(interceptors);
|
restTemplate.setInterceptors(interceptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package org.springframework.cloud.alibaba.dubbo.client.loadbalancer;
|
package org.springframework.cloud.alibaba.dubbo.client.loadbalancer;
|
||||||
|
|
||||||
import com.alibaba.dubbo.config.spring.ReferenceBean;
|
import com.alibaba.dubbo.config.spring.ReferenceBean;
|
||||||
|
import com.alibaba.dubbo.rpc.service.GenericException;
|
||||||
import com.alibaba.dubbo.rpc.service.GenericService;
|
import com.alibaba.dubbo.rpc.service.GenericService;
|
||||||
|
|
||||||
import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata;
|
import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata;
|
||||||
@ -53,12 +54,16 @@ public class DubboAdapterLoadBalancerInterceptor implements ClientHttpRequestInt
|
|||||||
|
|
||||||
private final List<HttpMessageConverter<?>> messageConverters;
|
private final List<HttpMessageConverter<?>> messageConverters;
|
||||||
|
|
||||||
|
private final DubboClientHttpResponseFactory clientHttpResponseFactory;
|
||||||
|
|
||||||
public DubboAdapterLoadBalancerInterceptor(DubboServiceMetadataRepository dubboServiceMetadataRepository,
|
public DubboAdapterLoadBalancerInterceptor(DubboServiceMetadataRepository dubboServiceMetadataRepository,
|
||||||
LoadBalancerInterceptor loadBalancerInterceptor,
|
LoadBalancerInterceptor loadBalancerInterceptor,
|
||||||
List<HttpMessageConverter<?>> messageConverters) {
|
List<HttpMessageConverter<?>> messageConverters,
|
||||||
|
ClassLoader classLoader) {
|
||||||
this.repository = dubboServiceMetadataRepository;
|
this.repository = dubboServiceMetadataRepository;
|
||||||
this.loadBalancerInterceptor = loadBalancerInterceptor;
|
this.loadBalancerInterceptor = loadBalancerInterceptor;
|
||||||
this.messageConverters = messageConverters;
|
this.messageConverters = messageConverters;
|
||||||
|
this.clientHttpResponseFactory = new DubboClientHttpResponseFactory(messageConverters, classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -72,30 +77,44 @@ public class DubboAdapterLoadBalancerInterceptor implements ClientHttpRequestInt
|
|||||||
|
|
||||||
repository.initialize(serviceName);
|
repository.initialize(serviceName);
|
||||||
|
|
||||||
RequestMetadata requestMetadata = buildRequestMetadata(request, uriComponents);
|
RequestMetadata clientMetadata = buildRequestMetadata(request, uriComponents);
|
||||||
|
|
||||||
ReferenceBean<GenericService> referenceBean =
|
RestMethodMetadata restMethodMetadata = repository.getRestMethodMetadata(serviceName, clientMetadata);
|
||||||
repository.getReferenceBean(serviceName, requestMetadata);
|
|
||||||
|
|
||||||
RestMethodMetadata restMethodMetadata = repository.getRestMethodMetadata(serviceName, requestMetadata);
|
ReferenceBean<GenericService> referenceBean = repository.getReferenceBean(serviceName, clientMetadata);
|
||||||
|
|
||||||
if (referenceBean == null || restMethodMetadata == null) {
|
if (referenceBean == null || restMethodMetadata == null) {
|
||||||
return loadBalancerInterceptor.intercept(request, body, execution);
|
return loadBalancerInterceptor.intercept(request, body, execution);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object result = null;
|
||||||
|
GenericException exception = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = invokeService(restMethodMetadata, referenceBean, clientMetadata);
|
||||||
|
} catch (GenericException e) {
|
||||||
|
exception = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientHttpResponseFactory.build(result, exception, clientMetadata, restMethodMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object invokeService(RestMethodMetadata restMethodMetadata, ReferenceBean<GenericService> referenceBean,
|
||||||
|
RequestMetadata clientMetadata) throws GenericException {
|
||||||
|
|
||||||
MethodMetadata methodMetadata = restMethodMetadata.getMethod();
|
MethodMetadata methodMetadata = restMethodMetadata.getMethod();
|
||||||
|
|
||||||
String methodName = methodMetadata.getName();
|
String methodName = methodMetadata.getName();
|
||||||
|
|
||||||
String[] parameterTypes = parameterResolver.resolveParameterTypes(methodMetadata);
|
String[] parameterTypes = parameterResolver.resolveParameterTypes(methodMetadata);
|
||||||
|
|
||||||
Object[] parameters = parameterResolver.resolveParameters(restMethodMetadata, request, uriComponents);
|
Object[] parameters = parameterResolver.resolveParameters(restMethodMetadata, clientMetadata);
|
||||||
|
|
||||||
GenericService genericService = referenceBean.get();
|
GenericService genericService = referenceBean.get();
|
||||||
|
|
||||||
Object result = genericService.$invoke(methodName, parameterTypes, parameters);
|
Object result = genericService.$invoke(methodName, parameterTypes, parameters);
|
||||||
|
|
||||||
return null;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RequestMetadata buildRequestMetadata(HttpRequest request, UriComponents uriComponents) {
|
public static RequestMetadata buildRequestMetadata(HttpRequest request, UriComponents uriComponents) {
|
||||||
|
@ -33,21 +33,19 @@ import java.io.InputStream;
|
|||||||
*/
|
*/
|
||||||
class DubboClientHttpResponse implements ClientHttpResponse {
|
class DubboClientHttpResponse implements ClientHttpResponse {
|
||||||
|
|
||||||
private final Object result;
|
|
||||||
|
|
||||||
private final GenericException exception;
|
|
||||||
|
|
||||||
private final HttpStatus httpStatus;
|
private final HttpStatus httpStatus;
|
||||||
|
|
||||||
private final String statusText;
|
private final String statusText;
|
||||||
|
|
||||||
private final HttpHeaders httpHeaders = new HttpHeaders();
|
private final HttpHeaders httpHeaders = new HttpHeaders();
|
||||||
|
|
||||||
public DubboClientHttpResponse(Object result, GenericException exception) {
|
private final DubboHttpOutputMessage httpOutputMessage;
|
||||||
this.result = result;
|
|
||||||
this.exception = exception;
|
public DubboClientHttpResponse(DubboHttpOutputMessage httpOutputMessage, GenericException exception) {
|
||||||
this.httpStatus = exception != null ? HttpStatus.INTERNAL_SERVER_ERROR : HttpStatus.OK;
|
this.httpStatus = exception != null ? HttpStatus.INTERNAL_SERVER_ERROR : HttpStatus.OK;
|
||||||
this.statusText = exception != null ? exception.getExceptionMessage() : httpStatus.getReasonPhrase();
|
this.statusText = exception != null ? exception.getExceptionMessage() : httpStatus.getReasonPhrase();
|
||||||
|
this.httpOutputMessage = httpOutputMessage;
|
||||||
|
this.httpHeaders.putAll(httpOutputMessage.getHeaders());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -67,12 +65,11 @@ class DubboClientHttpResponse implements ClientHttpResponse {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getBody() throws IOException {
|
public InputStream getBody() throws IOException {
|
||||||
return null;
|
return httpOutputMessage.getBody().getInputStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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.cloud.alibaba.dubbo.client.loadbalancer;
|
||||||
|
|
||||||
|
import com.alibaba.dubbo.rpc.service.GenericException;
|
||||||
|
|
||||||
|
import org.springframework.cloud.alibaba.dubbo.http.converter.HttpMessageConverterHolder;
|
||||||
|
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
|
||||||
|
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
|
||||||
|
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.HttpMessageConverterResolver;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.client.ClientHttpResponse;
|
||||||
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dubbo {@link ClientHttpResponse} Factory
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||||
|
*/
|
||||||
|
class DubboClientHttpResponseFactory {
|
||||||
|
|
||||||
|
private final HttpMessageConverterResolver httpMessageConverterResolver;
|
||||||
|
|
||||||
|
public DubboClientHttpResponseFactory(List<HttpMessageConverter<?>> messageConverters, ClassLoader classLoader) {
|
||||||
|
this.httpMessageConverterResolver = new HttpMessageConverterResolver(messageConverters, classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientHttpResponse build(Object result, GenericException exception,
|
||||||
|
RequestMetadata requestMetadata, RestMethodMetadata restMethodMetadata) {
|
||||||
|
|
||||||
|
DubboHttpOutputMessage httpOutputMessage = new DubboHttpOutputMessage();
|
||||||
|
|
||||||
|
HttpMessageConverterHolder httpMessageConverterHolder = httpMessageConverterResolver.resolve(requestMetadata, restMethodMetadata);
|
||||||
|
|
||||||
|
if (httpMessageConverterHolder != null) {
|
||||||
|
MediaType mediaType = httpMessageConverterHolder.getMediaType();
|
||||||
|
HttpMessageConverter converter = httpMessageConverterHolder.getConverter();
|
||||||
|
try {
|
||||||
|
converter.write(result, mediaType, httpOutputMessage);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DubboClientHttpResponse(httpOutputMessage, exception);
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,6 @@ import org.springframework.http.HttpOutputMessage;
|
|||||||
import org.springframework.util.FastByteArrayOutputStream;
|
import org.springframework.util.FastByteArrayOutputStream;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dubbo {@link HttpOutputMessage} implementation
|
* Dubbo {@link HttpOutputMessage} implementation
|
||||||
@ -31,7 +30,7 @@ import java.io.OutputStream;
|
|||||||
class DubboHttpOutputMessage implements HttpOutputMessage {
|
class DubboHttpOutputMessage implements HttpOutputMessage {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OutputStream getBody() throws IOException {
|
public FastByteArrayOutputStream getBody() throws IOException {
|
||||||
return new FastByteArrayOutputStream();
|
return new FastByteArrayOutputStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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.cloud.alibaba.dubbo.http.converter;
|
||||||
|
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link HttpMessageConverter} Holder with {@link MediaType}.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||||
|
*/
|
||||||
|
public class HttpMessageConverterHolder {
|
||||||
|
|
||||||
|
private final MediaType mediaType;
|
||||||
|
|
||||||
|
private final HttpMessageConverter<?> converter;
|
||||||
|
|
||||||
|
public HttpMessageConverterHolder(MediaType mediaType, HttpMessageConverter<?> converter) {
|
||||||
|
this.mediaType = mediaType;
|
||||||
|
this.converter = converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MediaType getMediaType() {
|
||||||
|
return mediaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpMessageConverter<?> getConverter() {
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
}
|
@ -21,12 +21,14 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||||||
import feign.RequestTemplate;
|
import feign.RequestTemplate;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -54,9 +56,9 @@ public class RequestMetadata {
|
|||||||
@JsonProperty("headers")
|
@JsonProperty("headers")
|
||||||
private HttpHeaders headers = new HttpHeaders();
|
private HttpHeaders headers = new HttpHeaders();
|
||||||
|
|
||||||
private List<String> consumes = new LinkedList<>();
|
private Set<String> consumes = new LinkedHashSet<>();
|
||||||
|
|
||||||
private List<String> produces = new LinkedList<>();
|
private Set<String> produces = new LinkedHashSet<>();
|
||||||
|
|
||||||
public RequestMetadata() {
|
public RequestMetadata() {
|
||||||
}
|
}
|
||||||
@ -100,19 +102,19 @@ public class RequestMetadata {
|
|||||||
headers(headers);
|
headers(headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getConsumes() {
|
public Set<String> getConsumes() {
|
||||||
return consumes;
|
return consumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConsumes(List<String> consumes) {
|
public void setConsumes(Set<String> consumes) {
|
||||||
this.consumes = consumes;
|
this.consumes = consumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getProduces() {
|
public Set<String> getProduces() {
|
||||||
return produces;
|
return produces;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProduces(List<String> produces) {
|
public void setProduces(Set<String> produces) {
|
||||||
this.produces = produces;
|
this.produces = produces;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,6 +139,14 @@ public class RequestMetadata {
|
|||||||
return toMediaTypes(produces);
|
return toMediaTypes(produces);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getParameter(String name) {
|
||||||
|
return this.params.getFirst(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHeader(String name) {
|
||||||
|
return this.headers.getFirst(name);
|
||||||
|
}
|
||||||
|
|
||||||
public RequestMetadata addParam(String name, String value) {
|
public RequestMetadata addParam(String name, String value) {
|
||||||
add(name, value, this.params);
|
add(name, value, this.params);
|
||||||
return this;
|
return this;
|
||||||
@ -153,13 +163,15 @@ public class RequestMetadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private <T extends Collection<String>> RequestMetadata headers(Map<String, T> headers) {
|
private <T extends Collection<String>> RequestMetadata headers(Map<String, T> headers) {
|
||||||
HttpHeaders httpHeaders = new HttpHeaders();
|
if (!CollectionUtils.isEmpty(headers)) {
|
||||||
// Add all headers
|
HttpHeaders httpHeaders = new HttpHeaders();
|
||||||
addAll(headers, httpHeaders);
|
// Add all headers
|
||||||
// Handles "Content-Type" and "Accept" headers if present
|
addAll(headers, httpHeaders);
|
||||||
mediaTypes(httpHeaders, HttpHeaders.CONTENT_TYPE, this.consumes);
|
// Handles "Content-Type" and "Accept" headers if present
|
||||||
mediaTypes(httpHeaders, HttpHeaders.ACCEPT, this.produces);
|
mediaTypes(httpHeaders, HttpHeaders.CONTENT_TYPE, this.consumes);
|
||||||
this.headers.putAll(httpHeaders);
|
mediaTypes(httpHeaders, HttpHeaders.ACCEPT, this.produces);
|
||||||
|
this.headers.putAll(httpHeaders);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +211,7 @@ public class RequestMetadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void mediaTypes(HttpHeaders httpHeaders, String headerName, List<String> destination) {
|
private static void mediaTypes(HttpHeaders httpHeaders, String headerName, Collection<String> destination) {
|
||||||
List<String> value = httpHeaders.get(headerName);
|
List<String> value = httpHeaders.get(headerName);
|
||||||
List<MediaType> mediaTypes = parseMediaTypes(value);
|
List<MediaType> mediaTypes = parseMediaTypes(value);
|
||||||
destination.addAll(toMediaTypeValues(mediaTypes));
|
destination.addAll(toMediaTypeValues(mediaTypes));
|
||||||
@ -213,11 +225,11 @@ public class RequestMetadata {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<MediaType> toMediaTypes(List<String> mediaTypeValues) {
|
private static List<MediaType> toMediaTypes(Collection<String> mediaTypeValues) {
|
||||||
if (mediaTypeValues.isEmpty()) {
|
if (mediaTypeValues.isEmpty()) {
|
||||||
return Collections.singletonList(MediaType.ALL);
|
return Collections.singletonList(MediaType.ALL);
|
||||||
}
|
}
|
||||||
return parseMediaTypes(mediaTypeValues);
|
return parseMediaTypes(new LinkedList<>(mediaTypeValues));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,186 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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.cloud.alibaba.dubbo.metadata.resolver;
|
||||||
|
|
||||||
|
import org.springframework.cloud.alibaba.dubbo.http.converter.HttpMessageConverterHolder;
|
||||||
|
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
|
||||||
|
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
|
import org.springframework.http.server.ServletServerHttpRequest;
|
||||||
|
import org.springframework.http.server.ServletServerHttpResponse;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link HttpMessageConverter} Resolver
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||||
|
*/
|
||||||
|
public class HttpMessageConverterResolver {
|
||||||
|
|
||||||
|
private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application");
|
||||||
|
|
||||||
|
private final List<HttpMessageConverter<?>> messageConverters;
|
||||||
|
|
||||||
|
private final List<MediaType> allSupportedMediaTypes;
|
||||||
|
|
||||||
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
|
public HttpMessageConverterResolver(List<HttpMessageConverter<?>> messageConverters, ClassLoader classLoader) {
|
||||||
|
this.messageConverters = messageConverters;
|
||||||
|
this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters);
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the most match {@link HttpMessageConverter} from {@link RequestMetadata}
|
||||||
|
*
|
||||||
|
* @param requestMetadata {@link RequestMetadata}
|
||||||
|
* @param restMethodMetadata {@link RestMethodMetadata}
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public HttpMessageConverterHolder resolve(RequestMetadata requestMetadata, RestMethodMetadata restMethodMetadata) {
|
||||||
|
|
||||||
|
HttpMessageConverterHolder httpMessageConverterHolder = null;
|
||||||
|
|
||||||
|
Class<?> returnValueClass = resolveReturnValueClass(restMethodMetadata);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see AbstractMessageConverterMethodProcessor#writeWithMessageConverters(Object, MethodParameter, ServletServerHttpRequest, ServletServerHttpResponse)
|
||||||
|
*/
|
||||||
|
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(requestMetadata);
|
||||||
|
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(restMethodMetadata, returnValueClass);
|
||||||
|
|
||||||
|
Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
|
||||||
|
for (MediaType requestedType : requestedMediaTypes) {
|
||||||
|
for (MediaType producibleType : producibleMediaTypes) {
|
||||||
|
if (requestedType.isCompatibleWith(producibleType)) {
|
||||||
|
compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compatibleMediaTypes.isEmpty()) {
|
||||||
|
return httpMessageConverterHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MediaType> mediaTypes = new ArrayList<>(compatibleMediaTypes);
|
||||||
|
|
||||||
|
MediaType.sortBySpecificityAndQuality(mediaTypes);
|
||||||
|
|
||||||
|
MediaType selectedMediaType = null;
|
||||||
|
for (MediaType mediaType : mediaTypes) {
|
||||||
|
if (mediaType.isConcrete()) {
|
||||||
|
selectedMediaType = mediaType;
|
||||||
|
break;
|
||||||
|
} else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
|
||||||
|
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedMediaType != null) {
|
||||||
|
selectedMediaType = selectedMediaType.removeQualityValue();
|
||||||
|
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
|
||||||
|
if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {
|
||||||
|
httpMessageConverterHolder = new HttpMessageConverterHolder(selectedMediaType, messageConverter);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpMessageConverterHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> resolveReturnValueClass(RestMethodMetadata restMethodMetadata) {
|
||||||
|
String returnClassName = restMethodMetadata.getMethod().getReturnType();
|
||||||
|
return ClassUtils.resolveClassName(returnClassName, classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the {@link MediaType media-types}
|
||||||
|
*
|
||||||
|
* @param requestMetadata {@link RequestMetadata} from client side
|
||||||
|
* @return non-null {@link List}
|
||||||
|
*/
|
||||||
|
private List<MediaType> getAcceptableMediaTypes(RequestMetadata requestMetadata) {
|
||||||
|
return requestMetadata.getProduceMediaTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns
|
||||||
|
* the media types that can be produced: <ul> <li>The producible media types specified in the request mappings, or
|
||||||
|
* <li>Media types of configured converters that can write the specific return value, or <li>{@link MediaType#ALL}
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param restMethodMetadata {@link RestMethodMetadata} from server side
|
||||||
|
* @param returnValueClass the class of return value
|
||||||
|
* @return non-null {@link List}
|
||||||
|
*/
|
||||||
|
private List<MediaType> getProducibleMediaTypes(RestMethodMetadata restMethodMetadata, Class<?> returnValueClass) {
|
||||||
|
RequestMetadata serverRequestMetadata = restMethodMetadata.getRequest();
|
||||||
|
List<MediaType> mediaTypes = serverRequestMetadata.getProduceMediaTypes();
|
||||||
|
if (!CollectionUtils.isEmpty(mediaTypes)) { // Empty
|
||||||
|
return mediaTypes;
|
||||||
|
} else if (!this.allSupportedMediaTypes.isEmpty()) {
|
||||||
|
List<MediaType> result = new ArrayList<>();
|
||||||
|
for (HttpMessageConverter<?> converter : this.messageConverters) {
|
||||||
|
if (converter.canWrite(returnValueClass, null)) {
|
||||||
|
result.addAll(converter.getSupportedMediaTypes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return Collections.singletonList(MediaType.ALL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the media types
|
||||||
|
* supported by all provided message converters sorted by specificity via {@link
|
||||||
|
* MediaType#sortBySpecificity(List)}.
|
||||||
|
*
|
||||||
|
* @param messageConverters
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private List<MediaType> getAllSupportedMediaTypes(List<HttpMessageConverter<?>> messageConverters) {
|
||||||
|
Set<MediaType> allSupportedMediaTypes = new LinkedHashSet<MediaType>();
|
||||||
|
for (HttpMessageConverter<?> messageConverter : messageConverters) {
|
||||||
|
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
|
||||||
|
}
|
||||||
|
List<MediaType> result = new ArrayList<MediaType>(allSupportedMediaTypes);
|
||||||
|
MediaType.sortBySpecificity(result);
|
||||||
|
return Collections.unmodifiableList(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the more specific of the acceptable and the producible media types
|
||||||
|
* with the q-value of the former.
|
||||||
|
*/
|
||||||
|
private MediaType getMostSpecificMediaType(MediaType acceptType, MediaType produceType) {
|
||||||
|
MediaType produceTypeToUse = produceType.copyQualityValue(acceptType);
|
||||||
|
return (MediaType.SPECIFICITY_COMPARATOR.compare(acceptType, produceTypeToUse) <= 0 ? acceptType : produceTypeToUse);
|
||||||
|
}
|
||||||
|
}
|
@ -20,9 +20,7 @@ import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata;
|
|||||||
import org.springframework.cloud.alibaba.dubbo.metadata.MethodParameterMetadata;
|
import org.springframework.cloud.alibaba.dubbo.metadata.MethodParameterMetadata;
|
||||||
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
|
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
|
||||||
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
|
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
|
||||||
import org.springframework.http.HttpRequest;
|
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.web.util.UriComponents;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -37,8 +35,7 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public class ParameterResolver {
|
public class ParameterResolver {
|
||||||
|
|
||||||
|
public Object[] resolveParameters(RestMethodMetadata restMethodMetadata, RequestMetadata clientMetadata) {
|
||||||
public Object[] resolveParameters(RestMethodMetadata restMethodMetadata, HttpRequest request, UriComponents uriComponents) {
|
|
||||||
|
|
||||||
MethodMetadata methodMetadata = restMethodMetadata.getMethod();
|
MethodMetadata methodMetadata = restMethodMetadata.getMethod();
|
||||||
|
|
||||||
@ -56,26 +53,24 @@ public class ParameterResolver {
|
|||||||
|
|
||||||
String name = getName(indexToName, index);
|
String name = getName(indexToName, index);
|
||||||
|
|
||||||
parameters[index] = getValue(requestMetadata, request, uriComponents, name);
|
parameters[index] = getValue(requestMetadata, clientMetadata, name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getValue(RequestMetadata requestMetadata, HttpRequest request, UriComponents uriComponents, String name) {
|
private String getValue(RequestMetadata serverMetadata, RequestMetadata clientMetadata, String name) {
|
||||||
|
|
||||||
String value = null;
|
String value = null;
|
||||||
Set<String> paramNames = requestMetadata.getParamNames();
|
Set<String> paramNames = serverMetadata.getParamNames();
|
||||||
Set<String> headerNames = requestMetadata.getHeaderNames();
|
Set<String> headerNames = serverMetadata.getHeaderNames();
|
||||||
|
|
||||||
if (paramNames.contains(name)) {
|
if (paramNames.contains(name)) {
|
||||||
value = uriComponents.getQueryParams().getFirst(name);
|
value = clientMetadata.getParameter(name);
|
||||||
} else if (headerNames.contains(name)) {
|
} else if (headerNames.contains(name)) {
|
||||||
value = request.getHeaders().getFirst(name);
|
value = clientMetadata.getHeader(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getName(Map<Integer, Collection<String>> indexToName, int index) {
|
private String getName(Map<Integer, Collection<String>> indexToName, int index) {
|
||||||
|
@ -30,14 +30,12 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
|
|||||||
import org.springframework.cloud.openfeign.FeignClient;
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE;
|
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE;
|
||||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||||
|
|
||||||
@ -111,12 +109,15 @@ public class DubboSpringCloudBootstrap {
|
|||||||
@Bean
|
@Bean
|
||||||
public ApplicationRunner restTemplateRunner() {
|
public ApplicationRunner restTemplateRunner() {
|
||||||
return arguments -> {
|
return arguments -> {
|
||||||
System.out.println(restTemplate.getForEntity("http://spring-cloud-alibaba-dubbo/echo?message=小马哥", String.class));
|
ResponseEntity<String> entity = restTemplate.getForEntity("http://spring-cloud-alibaba-dubbo/echo?message=小马哥", String.class);
|
||||||
Map<String, Object> data = new HashMap<>();
|
System.out.println(entity.getHeaders());
|
||||||
data.put("name", "小马哥");
|
System.out.println(entity.getBody());
|
||||||
data.put("age", 33);
|
// Still issue
|
||||||
data.put("height", 173);
|
// Map<String, Object> data = new HashMap<>();
|
||||||
System.out.println(restTemplate.postForEntity("http://spring-cloud-alibaba-dubbo/toString", data, String.class));
|
// data.put("name", "小马哥");
|
||||||
|
// data.put("age", 33);
|
||||||
|
// data.put("height", 173);
|
||||||
|
// System.out.println(restTemplate.postForEntity("http://spring-cloud-alibaba-dubbo/toString", data, String.class));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user