View Javadoc
1   package cn.home1.oss.lib.errorhandle.internal.rpc;
2   
3   import static com.google.common.base.Charsets.UTF_8;
4   import static com.google.common.collect.Lists.newArrayList;
5   import static org.apache.commons.lang3.StringUtils.toEncodedString;
6   
7   import cn.home1.oss.lib.errorhandle.api.ResolvedError;
8   import cn.home1.oss.lib.errorhandle.api.ResolvedErrorException;
9   
10  import com.fasterxml.jackson.databind.ObjectMapper;
11  import cn.home1.oss.lib.common.JaxbUtils;
12  
13  import feign.codec.DecodeException;
14  
15  import lombok.Setter;
16  import lombok.extern.slf4j.Slf4j;
17  
18  import org.springframework.beans.factory.annotation.Autowired;
19  import org.springframework.http.HttpHeaders;
20  import org.springframework.http.HttpStatus;
21  import org.springframework.oxm.jaxb.Jaxb2Marshaller;
22  import org.springframework.util.StreamUtils;
23  import org.springframework.web.client.HttpClientErrorException;
24  import org.springframework.web.client.HttpServerErrorException;
25  
26  @Setter
27  @Slf4j
28  public class GenericFeignErrorDecoder implements feign.codec.ErrorDecoder {
29  
30    private final feign.codec.ErrorDecoder delegate = new Default();
31  
32    private final Jaxb2Marshaller jaxb2Marshaller = JaxbUtils.jaxb2Marshaller();
33    @Autowired
34    private ObjectMapper objectMapper;
35  
36    private static HttpHeaders cloneHeaders(final feign.Response response) {
37      final HttpHeaders responseHeaders = new HttpHeaders();
38      response.headers().entrySet().stream()
39          .forEach(entry -> responseHeaders.put(entry.getKey(), newArrayList(entry.getValue())));
40      return responseHeaders;
41    }
42  
43    private static byte[] responseBody(final feign.Response response) {
44      try {
45        return StreamUtils.copyToByteArray(response.body().asInputStream());
46      } catch (final Exception wrapped) {
47        log.info("Failed to process response body.", wrapped);
48        throw new DecodeException("Failed to process response body.", wrapped);
49      }
50    }
51  
52    @Override
53    public Exception decode(final String methodKey, final feign.Response response) {
54      if (log.isDebugEnabled()) {
55        log.debug("decode : {}", methodKey);
56      }
57  
58      final HttpHeaders responseHeaders = cloneHeaders(response);
59      final byte[] responseBody = responseBody(response);
60      final Exception result;
61      if (ResolvedErrorException.isResolvedError(responseHeaders)) {
62        final String info = toEncodedString(responseBody, UTF_8);
63        try {
64          // 如果被调用的服务配置了XmlHttpMessageConverter(使用XStreamMarshaller) 可能导致数据被包裹在<string></string>里面
65          final String json =
66              info.startsWith("<string>") ? JaxbUtils.unmarshal(this.jaxb2Marshaller, info, String.class) : info;
67          result = new ResolvedErrorException(this.objectMapper.readValue(json, ResolvedError.class));
68        } catch (final Exception wrapped) {
69          log.info("Failed to decode resolvedError. info: " + info, wrapped);
70          throw new DecodeException("Failed to decode resolvedError. info: " + info, wrapped);
71        }
72      } else {
73        final HttpStatus statusCode = HttpStatus.valueOf(response.status());
74        final String statusText = response.reason();
75        if (response.status() >= 400 && response.status() <= 499) {
76          result = new HttpClientErrorException( //
77              statusCode, //
78              statusText, //
79              responseHeaders, //
80              responseBody, //
81              null);
82        } else if (response.status() >= 500 && response.status() <= 599) {
83          result = new HttpServerErrorException( //
84              statusCode, //
85              statusText, //
86              responseHeaders, //
87              responseBody, //
88              null);
89        } else {
90          result = delegate.decode(methodKey, response);
91        }
92      }
93      return result;
94    }
95  }