Spring Cloud Feign鍘熺悊鍙婃€ц兘
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Cloud Feign鍘熺悊鍙婃€ц兘相关的知识,希望对你有一定的参考价值。
鏍囩锛?a href='http://www.mamicode.com/so/1/coding' title='coding'>coding
throwable 瑙e喅鏂规 纭欢 淇敼 invalid char psu time
浠€涔堟槸Feign锛?/a>
Feign 鐨勮嫳鏂囪〃鎰忎负“鍋囪锛屼吉瑁咃紝鍙樺舰”锛?鏄竴涓猦ttp璇锋眰璋冪敤鐨勮交閲忕骇妗嗘灦锛屽彲浠ヤ互Java鎺ュ彛娉ㄨВ鐨勬柟寮忚皟鐢℉ttp璇锋眰锛岃€屼笉鐢ㄥ儚Java涓€氳繃灏佽HTTP璇锋眰鎶ユ枃鐨勬柟寮忕洿鎺ヨ皟鐢ㄣ€侳eign閫氳繃澶勭悊娉ㄨВ锛屽皢璇锋眰妯℃澘鍖栵紝褰撳疄闄呰皟鐢ㄧ殑鏃跺€欙紝浼犲叆鍙傛暟锛屾牴鎹弬鏁板啀搴旂敤鍒拌姹備笂锛岃繘鑰岃浆鍖栨垚鐪熸鐨勮姹傦紝杩欑璇锋眰鐩稿鑰岃█姣旇緝鐩磋銆?br />
Feign琚箍娉涘簲鐢ㄥ湪Spring Cloud 鐨勮В鍐虫柟妗堜腑锛屾槸瀛︿範鍩轰簬Spring Cloud 寰湇鍔℃灦鏋勪笉鍙垨缂虹殑閲嶈缁勪欢銆?br />
寮€婧愰」鐩湴鍧€锛?br />
https://github.com/OpenFeign/feign
Feign瑙e喅浜嗕粈涔堥棶棰橈紵
灏佽浜咹ttp璋冪敤娴佺▼锛屾洿閫傚悎闈㈠悜鎺ュ彛鍖栫殑鍙樻垚涔犳儻
鍦ㄦ湇鍔¤皟鐢ㄧ殑鍦烘櫙涓紝鎴戜滑缁忓父璋冪敤鍩轰簬Http鍗忚鐨勬湇鍔★紝鑰屾垜浠粡甯镐娇鐢ㄥ埌鐨勬鏋跺彲鑳芥湁HttpURLConnection銆丄pache HttpComponnets銆丱kHttp3 銆丯etty绛夌瓑锛岃繖浜涙鏋跺湪鍩轰簬鑷韩鐨勪笓娉ㄧ偣鎻愪緵浜嗚嚜韬壒鎬с€傝€屼粠瑙掕壊鍒掑垎涓婃潵鐪嬶紝浠栦滑鐨勮亴鑳芥槸涓€鑷寸殑鎻愪緵Http璋冪敤鏈嶅姟銆傚叿浣撴祦绋嬪涓嬶細
Feign鏄浣曡璁$殑锛?/h2>
PHASE 1. 鍩轰簬闈㈠悜鎺ュ彛鐨勫姩鎬佷唬鐞嗘柟寮忕敓鎴愬疄鐜扮被
鍦ㄤ娇鐢╢eign 鏃讹紝浼氬畾涔夊搴旂殑鎺ュ彛绫伙紝鍦ㄦ帴鍙g被涓婁娇鐢℉ttp鐩稿叧鐨勬敞瑙o紝鏍囪瘑HTTP璇锋眰鍙傛暟淇℃伅,濡備笅鎵€绀猴細
interface GitHub {
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
}
public static class Contributor {
String login;
int contributions;
}
public class MyApp {
public static void main(String... args) {
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
// Fetch and print a list of the contributors to this library.
List<Contributor> contributors = github.contributors("OpenFeign", "feign");
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
}
}
鍦‵eign 搴曞眰锛岄€氳繃鍩轰簬闈㈠悜鎺ュ彛鐨勫姩鎬佷唬鐞嗘柟寮忕敓鎴愬疄鐜扮被锛屽皢璇锋眰璋冪敤濮旀墭鍒板姩鎬佷唬鐞嗗疄鐜扮被锛屽熀鏈師鐞嗗涓嬫墍绀猴細
1 public class ReflectiveFeign extends Feign{
2 ///鐪佺暐閮ㄥ垎浠g爜
3 @Override
4 public <T> T newInstance(Target<T> target) {
5 //鏍规嵁鎺ュ彛绫诲拰Contract鍗忚瑙f瀽鏂瑰紡锛岃В鏋愭帴鍙g被涓婄殑鏂规硶鍜屾敞瑙o紝杞崲鎴愬唴閮ㄧ殑MethodHandler澶勭悊鏂瑰紡
6 Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
7 Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
8 List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
9
10 for (Method method : target.type().getMethods()) {
11 if (method.getDeclaringClass() == Object.class) {
12 continue;
13 } else if(Util.isDefault(method)) {
14 DefaultMethodHandler handler = new DefaultMethodHandler(method);
15 defaultMethodHandlers.add(handler);
16 methodToHandler.put(method, handler);
17 } else {
18 methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
19 }
20 }
21 InvocationHandler handler = factory.create(target, methodToHandler);
22 // 鍩轰簬Proxy.newProxyInstance 涓烘帴鍙g被鍒涘缓鍔ㄦ€佸疄鐜帮紝灏嗘墍鏈夌殑璇锋眰杞崲缁橧nvocationHandler 澶勭悊銆?/span>
23 T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
24
25 for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
26 defaultMethodHandler.bindTo(proxy);
27 }
28 return proxy;
29 }
30 //鐪佺暐閮ㄥ垎浠g爜
PHASE 2. 鏍规嵁Contract鍗忚瑙勫垯锛岃В鏋愭帴鍙g被鐨勬敞瑙d俊鎭紝瑙f瀽鎴愬唴閮ㄨ〃鐜帮細
Feign 瀹氫箟浜嗚浆鎹㈠崗璁紝瀹氫箟濡備笅锛?/p>
/**
* Defines what annotations and values are valid on interfaces.
*/
public interface Contract {
/**
* Called to parse the methods in the class that are linked to HTTP requests.
* 浼犲叆鎺ュ彛瀹氫箟锛岃В鏋愭垚鐩稿簲鐨勬柟娉曞唴閮ㄥ厓鏁版嵁琛ㄧず
* @param targetType {@link feign.Target#type() type} of the Feign interface.
*/
// TODO: break this and correct spelling at some point
List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType);
}
榛樿Contract 瀹炵幇
Feign 榛樿鏈変竴濂楄嚜宸辩殑鍗忚瑙勮寖锛岃瀹氫簡涓€浜涙敞瑙o紝鍙互鏄犲皠鎴愬搴旂殑Http璇锋眰锛屽瀹樻柟鐨勪竴涓緥瀛愶細
public interface GitHub {
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> getContributors(@Param("owner") String owner, @Param("repo") String repository);
class Contributor {
String login;
int contributions;
}
}
涓婅堪鐨勪緥瀛愪腑锛屽皾璇曡皟鐢℅itHub.getContributors("foo","myrepo")鐨勭殑鏃跺€欙紝浼氳浆鎹㈡垚濡備笅鐨凥TTP璇锋眰锛?/span>
GET /repos/foo/myrepo/contributors
HOST XXXX.XXX.XXX
Feign 榛樿鐨勫崗璁鑼?/code>
娉ㄨВ 鎺ュ彛Target 浣跨敤璇存槑
@RequestLine
鏂规硶涓?/td>
瀹氫箟HttpMethod 鍜?UriTemplate. UriTemplate 涓娇鐢?code>{} 鍖呰9鐨勮〃杈惧紡锛屽彲浠ラ€氳繃鍦ㄦ柟娉曞弬鏁颁笂浣跨敤@Param 鑷姩娉ㄥ叆
@Param
鏂规硶鍙傛暟
瀹氫箟妯℃澘鍙橀噺锛屾ā鏉垮彉閲忕殑鍊煎彲浠ヤ娇鐢ㄥ悕绉扮殑鏂瑰紡浣跨敤妯℃澘娉ㄥ叆瑙f瀽
@Headers
绫讳笂鎴栬€呮柟娉曚笂
瀹氫箟澶撮儴妯℃澘鍙橀噺锛屼娇鐢ˊParam 娉ㄨВ鎻愪緵鍙傛暟鍊肩殑娉ㄥ叆銆傚鏋滆娉ㄨВ娣诲姞鍦ㄦ帴鍙g被涓婏紝鍒欐墍鏈夌殑璇锋眰閮戒細鎼哄甫瀵瑰簲鐨凥eader淇℃伅锛涘鏋滃湪鏂规硶涓婏紝鍒欏彧浼氭坊鍔犲埌瀵瑰簲鐨勬柟娉曡姹備笂
@QueryMap
鏂规硶涓?/td>
瀹氫箟涓€涓敭鍊煎鎴栬€?pojo锛屽弬鏁板€煎皢浼氳杞崲鎴怳RL涓婄殑 query 瀛楃涓蹭笂
@HeaderMap
鏂规硶涓?/td>
瀹氫箟涓€涓狧eaderMap, 涓?UrlTemplate 鍜孒eaderTemplate 绫诲瀷锛屽彲浠ヤ娇鐢ˊParam 娉ㄨВ鎻愪緵鍙傛暟鍊?/td>
鍏蜂綋FeignContract 鏄浣曡В鏋愮殑锛屼笉鍦ㄦ湰鏂囩殑浠嬬粛鑼冨洿鍐咃紝璇︽儏璇峰弬鑰冧唬鐮侊細
https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/Contract.java
鍩轰簬Spring MVC鐨勫崗璁鑼僑pringMvcContract:
褰撳墠Spring Cloud 寰湇鍔¤В鍐虫柟妗堜腑锛屼负浜嗛檷浣庡涔犳垚鏈紝閲囩敤浜哠pring MVC鐨勯儴鍒嗘敞瑙f潵瀹屾垚 璇锋眰鍗忚瑙f瀽锛屼篃灏辨槸璇?锛屽啓瀹㈡埛绔姹傛帴鍙e拰鍍忓啓鏈嶅姟绔唬鐮佷竴鏍凤細瀹㈡埛绔拰鏈嶅姟绔彲浠ラ€氳繃SDK鐨勬柟寮忚繘琛岀害瀹氾紝瀹㈡埛绔彧闇€瑕佸紩鍏ユ湇鍔$鍙戝竷鐨凷DK API锛屽氨鍙互浣跨敤闈㈠悜鎺ュ彛鐨勭紪鐮佹柟寮忓鎺ユ湇鍔★細
鎴戜滑鍥㈤槦鍐呴儴灏辨槸鎸夌収杩欑鎬濊矾锛岀粨鍚圫pring Boot Starter 鐨勭壒鎬э紝瀹氫箟浜嗘湇鍔$starter,
鏈嶅姟娑堣垂鑰呭湪浣跨敤鐨勬椂鍊欙紝鍙渶瑕佸紩鍏tarter锛屽氨鍙互璋冪敤鏈嶅姟銆傝繖涓瘮杈冮€傚悎骞冲彴鏃犲叧鎬э紝鎺ュ彛鎶借薄鍑烘潵鐨勫ソ澶勫氨鏄彲浠ユ牴鎹湇鍔¤皟鐢ㄥ疄鐜版柟寮忚嚜鏈夊垏鎹細
- 鍙互鍩轰簬绠€鍗曠殑Http鏈嶅姟璋冪敤锛?/li>
- 鍙互鍩轰簬Spring Cloud 寰湇鍔℃灦鏋勮皟鐢紱
- 鍙互鍩轰簬Dubbo SOA鏈嶅姟娌荤悊
杩欑妯″紡姣旇緝閫傚悎鍦⊿aSS娣峰悎杞欢鏈嶅姟鐨勬ā寮忎笅鑷湁鍒囨崲锛屾牴鎹鎴风殑纭欢鑳藉姏閫夋嫨鍚堥€傜殑鏂瑰紡閮ㄧ讲锛屼篃鍙互鍩轰簬鑷韩鐨勬湇鍔¢泦缇ら儴缃插井鏈嶅姟
鑷充簬Spring Cloud 鏄浣曞疄鐜?鍗忚瑙f瀽鐨勶紝鍙弬鑰冧唬鐮侊細
https://github.com/spring-cloud/spring-cloud-openfeign/blob/master/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java
褰撶劧锛岀洰鍓嶇殑Spring MVC鐨勬敞瑙e苟涓嶆槸鍙互瀹屽叏浣跨敤鐨勶紝鏈変竴浜涙敞瑙e苟涓嶆敮鎸?濡?code>@GetMapping,@PutMapping
绛夛紝浠呮敮鎸佷娇鐢?code>@RequestMapping 绛夛紝鍙﹀娉ㄨВ缁ф壙鎬ф柟闈篃鏈変簺闂锛涘叿浣撻檺鍒剁粏鑺傦紝姣忎釜鐗堟湰鑳戒細鏈変簺鍑哄叆锛屽彲浠ュ弬鑰冧笂杩扮殑浠g爜瀹炵幇锛屾瘮杈冪畝鍗曘€?/p>
Spring Cloud 娌℃湁鍩轰簬Spring MVC 鍏ㄩ儴娉ㄨВ鏉ュ仛Feign 瀹㈡埛绔敞瑙e崗璁В鏋愶紝涓汉璁や负杩欎釜鏄竴涓笉灏忕殑鍧戙€傚湪鍒氬叆鎵婼pring Cloud 鐨勬椂鍊欙紝灏辩鍒拌繖涓棶棰樸€傚悗鏉ユ槸娣卞叆浠g爜鎵嶈В鍐崇殑.... 杩欎釜搴旇鏈変汉鍐欎簡澧炲己绫绘潵澶勭悊锛屾殏涓斾笉琛紝鍏圡ARK涓€涓嬶紝鏄竴涓紑婧愪唬鐮佺粌鎵嬬殑濂芥満浼氥€?/p>
PHASE 3. 鍩轰簬 RequestBean锛屽姩鎬佺敓鎴怰equest
鏍规嵁浼犲叆鐨凚ean瀵硅薄鍜屾敞瑙d俊鎭紝浠庝腑鎻愬彇鍑虹浉搴旂殑鍊硷紝鏉ユ瀯閫燞ttp Request 瀵硅薄锛?/p>
PHASE 4. 浣跨敤Encoder 灏咮ean杞崲鎴?Http鎶ユ枃姝f枃锛堟秷鎭В鏋愬拰杞爜閫昏緫锛?/h5>
Feign 鏈€缁堜細灏嗚姹傝浆鎹㈡垚Http 娑堟伅鍙戦€佸嚭鍘伙紝浼犲叆鐨勮姹傚璞℃渶缁堜細瑙f瀽鎴愭秷鎭綋锛屽涓嬫墍绀猴細
鍦ㄦ帴鍙e畾涔変笂Feign鍋氱殑姣旇緝绠€鍗曪紝鎶借薄鍑轰簡Encoder 鍜宒ecoder 鎺ュ彛锛?/p>
public interface Encoder {
/** Type literal for {@code Map<String, ?>}, indicating the object to encode is a form. */
Type MAP_STRING_WILDCARD = Util.MAP_STRING_WILDCARD;
/**
* Converts objects to an appropriate representation in the template.
* 灏嗗疄浣撳璞¤浆鎹㈡垚Http璇锋眰鐨勬秷鎭鏂囦腑
* @param object what to encode as the request body.
* @param bodyType the type the object should be encoded as. {@link #MAP_STRING_WILDCARD}
* indicates form encoding.
* @param template the request template to populate.
* @throws EncodeException when encoding failed due to a checked exception.
*/
void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;
/**
* Default implementation of {@code Encoder}.
*/
class Default implements Encoder {
@Override
public void encode(Object object, Type bodyType, RequestTemplate template) {
if (bodyType == String.class) {
template.body(object.toString());
} else if (bodyType == byte[].class) {
template.body((byte[]) object, null);
} else if (object != null) {
throw new EncodeException(
format("%s is not a type supported by this encoder.", object.getClass()));
}
}
}
}
public interface Decoder {
/**
* Decodes an http response into an object corresponding to its {@link
* java.lang.reflect.Method#getGenericReturnType() generic return type}. If you need to wrap
* exceptions, please do so via {@link DecodeException}.
* 浠嶳esponse 涓彁鍙朒ttp娑堟伅姝f枃锛岄€氳繃鎺ュ彛绫诲0鏄庣殑杩斿洖绫诲瀷锛屾秷鎭嚜鍔ㄨ閰?
* @param response the response to decode
* @param type {@link java.lang.reflect.Method#getGenericReturnType() generic return type} of
* the method corresponding to this {@code response}.
* @return instance of {@code type}
* @throws IOException will be propagated safely to the caller.
* @throws DecodeException when decoding failed due to a checked exception besides IOException.
* @throws FeignException when decoding succeeds, but conveys the operation failed.
*/
Object decode(Response response, Type type) throws IOException, DecodeException, FeignException;
/** Default implementation of {@code Decoder}. */
public class Default extends StringDecoder {
@Override
public Object decode(Response response, Type type) throws IOException {
if (response.status() == 404) return Util.emptyValueOf(type);
if (response.body() == null) return null;
if (byte[].class.equals(type)) {
return Util.toByteArray(response.body().asInputStream());
}
return super.decode(response, type);
}
}
}
鐩墠Feign 鏈変互涓嬪疄鐜帮細
Encoder/ Decoder 瀹炵幇 璇存槑
JacksonEncoder锛孞acksonDecoder
鍩轰簬 Jackson 鏍煎紡鐨勬寔涔呭寲杞崲鍗忚
GsonEncoder锛孏sonDecoder
鍩轰簬Google GSON 鏍煎紡鐨勬寔涔呭寲杞崲鍗忚
SaxEncoder锛孲axDecoder
鍩轰簬XML 鏍煎紡鐨凷ax 搴撴寔涔呭寲杞崲鍗忚
JAXBEncoder锛孞AXBDecoder
鍩轰簬XML 鏍煎紡鐨凧AXB 搴撴寔涔呭寲杞崲鍗忚
ResponseEntityEncoder锛孯esponseEntityDecoder
Spring MVC 鍩轰簬 ResponseEntity< T > 杩斿洖鏍煎紡鐨勮浆鎹㈠崗璁?/td>
SpringEncoder锛孲pringDecoder
鍩轰簬Spring MVC HttpMessageConverters 涓€濂楁満鍒跺疄鐜扮殑杞崲鍗忚 ,搴旂敤浜嶴pring Cloud 浣撶郴涓?/td>
PHASE 5. 鎷︽埅鍣ㄨ礋璐e璇锋眰鍜岃繑鍥炶繘琛岃楗板鐞?/h5>
鍦ㄨ姹傝浆鎹㈢殑杩囩▼涓紝Feign 鎶借薄鍑烘潵浜嗘嫤鎴櫒鎺ュ彛锛岀敤浜庣敤鎴疯嚜瀹氫箟瀵硅姹傜殑鎿嶄綔锛?/p>
public interface RequestInterceptor {
/**
* 鍙互鍦ㄦ瀯閫燫equestTemplate 璇锋眰鏃讹紝澧炲姞鎴栬€呬慨鏀笻eader, Method, Body 绛変俊鎭?
* Called for every request. Add data using methods on the supplied {@link RequestTemplate}.
*/
void apply(RequestTemplate template);
}
姣斿锛屽鏋滃笇鏈汬ttp娑堟伅浼犻€掕繃绋嬩腑琚帇缂╋紝鍙互瀹氫箟涓€涓姹傛嫤鎴櫒锛?/pre>
public class FeignAcceptGzipEncodingInterceptor extends BaseRequestInterceptor {
/**
* Creates new instance of {@link FeignAcceptGzipEncodingInterceptor}.
*
* @param properties the encoding properties
*/
protected FeignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
super(properties);
}
/**
* {@inheritDoc}
*/
@Override
public void apply(RequestTemplate template) {
// 鍦℉eader 澶撮儴娣诲姞鐩稿簲鐨勬暟鎹俊鎭?/span>
addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER, HttpEncoding.GZIP_ENCODING,
HttpEncoding.DEFLATE_ENCODING);
}
}
PHASE 6. 鏃ュ織璁板綍
鍦ㄥ彂閫佸拰鎺ユ敹璇锋眰鐨勬椂鍊欙紝Feign瀹氫箟浜嗙粺涓€鐨勬棩蹇楅棬闈㈡潵杈撳嚭鏃ュ織淇℃伅 , 骞朵笖灏嗘棩蹇楃殑杈撳嚭瀹氫箟浜嗗洓涓瓑绾э細
绾у埆 璇存槑
NONE
涓嶅仛浠讳綍璁板綍
BASIC
鍙褰曡緭鍑篐ttp 鏂规硶鍚嶇О銆佽姹俇RL銆佽繑鍥炵姸鎬佺爜鍜屾墽琛屾椂闂?/td>
HEADERS
璁板綍杈撳嚭Http 鏂规硶鍚嶇О銆佽姹俇RL銆佽繑鍥炵姸鎬佺爜鍜屾墽琛屾椂闂?鍜?Header 淇℃伅
FULL
璁板綍Request 鍜孯esponse鐨凥eader锛孊ody鍜屼竴浜涜姹傚厓鏁版嵁
public abstract class Logger {
protected static String methodTag(String configKey) {
return new StringBuilder().append(鈥榌鈥?.append(configKey.substring(0, configKey.indexOf(鈥?鈥?span style="color: #000000;">)))
.append("] ").toString();
}
/**
* Override to log requests and responses using your own implementation. Messages will be http
* request and response text.
*
* @param configKey value of {@link Feign#configKey(Class, java.lang.reflect.Method)}
* @param format {@link java.util.Formatter format string}
* @param args arguments applied to {@code format}
*/
protected abstract void log(String configKey, String format, Object... args);
protected void logRequest(String configKey, Level logLevel, Request request) {
log(configKey, "---> %s %s HTTP/1.1", request.method(), request.url());
if (logLevel.ordinal() >= Level.HEADERS.ordinal()) {
for (String field : request.headers().keySet()) {
for (String value : valuesOrEmpty(request.headers(), field)) {
log(configKey, "%s: %s", field, value);
}
}
int bodyLength = 0;
if (request.body() != null) {
bodyLength = request.body().length;
if (logLevel.ordinal() >= Level.FULL.ordinal()) {
String
bodyText =
request.charset() != null ? new String(request.body(), request.charset()) : null;
log(configKey, ""); // CRLF
log(configKey, "%s", bodyText != null ? bodyText : "Binary data");
}
}
log(configKey, "---> END HTTP (%s-byte body)", bodyLength);
}
}
protected void logRetry(String configKey, Level logLevel) {
log(configKey, "---> RETRYING");
}
protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response,
long elapsedTime) throws IOException {
String reason = response.reason() != null && logLevel.compareTo(Level.NONE) > 0 ?
" " + response.reason() : "";
int status = response.status();
log(configKey, "<--- HTTP/1.1 %s%s (%sms)", status, reason, elapsedTime);
if (logLevel.ordinal() >= Level.HEADERS.ordinal()) {
for (String field : response.headers().keySet()) {
for (String value : valuesOrEmpty(response.headers(), field)) {
log(configKey, "%s: %s", field, value);
}
}
int bodyLength = 0;
if (response.body() != null && !(status == 204 || status == 205)) {
// HTTP 204 No Content "...response MUST NOT include a message-body"
// HTTP 205 Reset Content "...response MUST NOT include an entity"
if (logLevel.ordinal() >= Level.FULL.ordinal()) {
log(configKey, ""); // CRLF
}
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
bodyLength = bodyData.length;
if (logLevel.ordinal() >= Level.FULL.ordinal() && bodyLength > 0) {
log(configKey, "%s", decodeOrDefault(bodyData, UTF_8, "Binary data"));
}
log(configKey, "<--- END HTTP (%s-byte body)", bodyLength);
return response.toBuilder().body(bodyData).build();
} else {
log(configKey, "<--- END HTTP (%s-byte body)", bodyLength);
}
}
return response;
}
protected IOException logIOException(String configKey, Level logLevel, IOException ioe, long elapsedTime) {
log(configKey, "<--- ERROR %s: %s (%sms)", ioe.getClass().getSimpleName(), ioe.getMessage(),
elapsedTime);
if (logLevel.ordinal() >= Level.FULL.ordinal()) {
StringWriter sw = new StringWriter();
ioe.printStackTrace(new PrintWriter(sw));
log(configKey, sw.toString());
log(configKey, "<--- END ERROR");
}
return ioe;
}