GenericFeignErrorDecoder.java
package cn.home1.oss.lib.errorhandle.internal.rpc;
import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.collect.Lists.newArrayList;
import static org.apache.commons.lang3.StringUtils.toEncodedString;
import cn.home1.oss.lib.errorhandle.api.ResolvedError;
import cn.home1.oss.lib.errorhandle.api.ResolvedErrorException;
import com.fasterxml.jackson.databind.ObjectMapper;
import cn.home1.oss.lib.common.JaxbUtils;
import feign.codec.DecodeException;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
@Setter
@Slf4j
public class GenericFeignErrorDecoder implements feign.codec.ErrorDecoder {
private final feign.codec.ErrorDecoder delegate = new Default();
private final Jaxb2Marshaller jaxb2Marshaller = JaxbUtils.jaxb2Marshaller();
@Autowired
private ObjectMapper objectMapper;
private static HttpHeaders cloneHeaders(final feign.Response response) {
final HttpHeaders responseHeaders = new HttpHeaders();
response.headers().entrySet().stream()
.forEach(entry -> responseHeaders.put(entry.getKey(), newArrayList(entry.getValue())));
return responseHeaders;
}
private static byte[] responseBody(final feign.Response response) {
try {
return StreamUtils.copyToByteArray(response.body().asInputStream());
} catch (final Exception wrapped) {
log.info("Failed to process response body.", wrapped);
throw new DecodeException("Failed to process response body.", wrapped);
}
}
@Override
public Exception decode(final String methodKey, final feign.Response response) {
if (log.isDebugEnabled()) {
log.debug("decode : {}", methodKey);
}
final HttpHeaders responseHeaders = cloneHeaders(response);
final byte[] responseBody = responseBody(response);
final Exception result;
if (ResolvedErrorException.isResolvedError(responseHeaders)) {
final String info = toEncodedString(responseBody, UTF_8);
try {
// 如果被调用的服务配置了XmlHttpMessageConverter(使用XStreamMarshaller) 可能导致数据被包裹在<string></string>里面
final String json =
info.startsWith("<string>") ? JaxbUtils.unmarshal(this.jaxb2Marshaller, info, String.class) : info;
result = new ResolvedErrorException(this.objectMapper.readValue(json, ResolvedError.class));
} catch (final Exception wrapped) {
log.info("Failed to decode resolvedError. info: " + info, wrapped);
throw new DecodeException("Failed to decode resolvedError. info: " + info, wrapped);
}
} else {
final HttpStatus statusCode = HttpStatus.valueOf(response.status());
final String statusText = response.reason();
if (response.status() >= 400 && response.status() <= 499) {
result = new HttpClientErrorException( //
statusCode, //
statusText, //
responseHeaders, //
responseBody, //
null);
} else if (response.status() >= 500 && response.status() <= 599) {
result = new HttpServerErrorException( //
statusCode, //
statusText, //
responseHeaders, //
responseBody, //
null);
} else {
result = delegate.decode(methodKey, response);
}
}
return result;
}
}