サービスマイクロRestTemplateエレガントな呼び出しAPI(インターセプタ、例外処理、メッセージ変換)を使用する方法

私は古典的な顔の質問だけでなく、マイクロ技術の共有サービス、心配です、あなたは、最新の知識を得ることができます

  マイクロサービスでは、restサービスコール同士は非常に一般的ですが、どのように我々は優雅に呼ばない、実際には、Springフレームワークが使用するRestTemplate優雅クラスすることができrest、互いを呼び出すためのサービス、それが簡単になり、http通信サービス、統一RESTful規格、カプセル化httpリンクは、操作が使用するのは簡単です、あなたもRestTemplateモードの必要な定義をカスタマイズすることができます。どこで:

  • RestTemplate既定のHttpMessageConverterインスタンスHTTPにメッセージPOJOまたはからPOJO変換されたHTTPメッセージ。デフォルトのマスターによって登録されているmimeコンバータの種類を、だけでなく、できるsetMessageConvertersカスタムコンバータを登録します。
  • RestTemplateデフォルト使用DefaultResponseErrorHandler40XのBad Requestか50X internal異常なerrorエラー情報の取得を。
  • RestTemplateまた、インターセプター使用することができinterceptor、リクエストリンクトラッキングで行われ、統一ヘッドの設定を。

これはRestTemplateまた、多数の定義RESTに対応ほとんどがインタラクティブリソースの方法HTTP以下のように、この方法は:

方法 解決
削除() HTTPは、特定のリソースのURLにDELETE操作の実行
交換() URLに特定のHTTPメソッドを実行し、ResponseEntityは、オブジェクトが含まれて返します
実行() 特定のHTTP URLで実行される方法では、反応マップから得られるオブジェクト本体を返します
getForEntity() HTTP GETリクエストを送信し、それはレスポンスボディResponseEntityはにマッピングされたオブジェクトが含まれて返します
getForObject() HTTP GETリクエストを送信し、その要求がターゲット本体にマッピングされる返します
postForEntity() URLへのPOSTデータは、ResponseEntityを備えたオブジェクトを返します。
postForObject() 、応答は、オブジェクト整合体が形成されているURLにPOSTデータを返します
headForHeaders() HTTP HEADリクエストを送信し、それが特定のリソースのURLを含むHTTPヘッダを返します
optionsForAllow() HTTPのOPTIONS要求を送信し、特定のURLの許可ヘッダ情報を返します
postForLocation() URLへのPOSTデータは、新しく作成されたリソースのURLを返します。
プット() 特定のURLへのリソースのPUT

1. RestTemplateソース

1.1デフォルトのコールのリンク

restTemplate場合は、API呼び出し、デフォルトのコールチェーン:

###########1.使用createRequest创建请求########
resttemplate->execute()->doExecute()
HttpAccessor->createRequest()
//获取拦截器Interceptor,InterceptingClientHttpRequestFactory,SimpleClientHttpRequestFactory
InterceptingHttpAccessor->getRequestFactory() 
//获取默认的SimpleBufferingClientHttpRequest
SimpleClientHttpRequestFactory->createRequest()

#######2.获取响应response进行处理###########
AbstractClientHttpRequest->execute()->executeInternal()
AbstractBufferingClientHttpRequest->executeInternal()

###########3.异常处理#####################
resttemplate->handleResponse()

##########4.响应消息体封装为java对象#######
HttpMessageConverterExtractor->extractData()
复制代码

1.2 restTemplate-> doExecute()

デフォルトのコールチェーンでは、restTemplateAPI呼び出しが呼び出すdoExecuteメソッドを、この方法は、以下のステップを実行することができる、主に次のとおりです。

1)使用してcreateRequest作成要求、フェッチレスポンス
)2 応答異常例外ハンドリングか否かを判断する
)3をJavaオブジェクト体としてカプセル化された応答メッセージを

@Nullable
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
		@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {

	Assert.notNull(url, "URI is required");
	Assert.notNull(method, "HttpMethod is required");
	ClientHttpResponse response = null;
	try {
		//使用createRequest创建请求
		ClientHttpRequest request = createRequest(url, method);
		if (requestCallback != null) {
			requestCallback.doWithRequest(request);
		}
		//获取响应response进行处理
		response = request.execute();
		//异常处理
		handleResponse(url, method, response);
		//响应消息体封装为java对象
		return (responseExtractor != null ? responseExtractor.extractData(response) : null);
	}catch (IOException ex) {
		String resource = url.toString();
		String query = url.getRawQuery();
		resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
		throw new ResourceAccessException("I/O error on " + method.name() +
				" request for \"" + resource + "\": " + ex.getMessage(), ex);
	}finally {
		if (response != null) {
			response.close();
		}
	}
}
复制代码

1.3 InterceptingHttpAccessor-> getRequestFactory()

デフォルトのコールチェーンでは、InterceptingHttpAccessor的getRequestFactory()方法は、ない場合interceptorインターセプター、デフォルトに戻ってくるSimpleClientHttpRequestFactory、そうでない場合は、返すInterceptingClientHttpRequestFactoryrequestFactory、できるresttemplate.setInterceptorsカスタムインターセプタを設定しますinterceptor

//Return the request factory that this accessor uses for obtaining client request handles.
public ClientHttpRequestFactory getRequestFactory() {
        //获取拦截器interceptor(自定义的)
		List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
		if (!CollectionUtils.isEmpty(interceptors)) {
			ClientHttpRequestFactory factory = this.interceptingRequestFactory;
			if (factory == null) {
				factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
				this.interceptingRequestFactory = factory;
			}
			return factory;
		}
		else {
			return super.getRequestFactory();
		}
	}
复制代码

その後、呼び出しSimpleClientHttpRequestFactory的createRequest接続を作成するには:

@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
	HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
	prepareConnection(connection, httpMethod.name());

	if (this.bufferRequestBody) {
		return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
	}
	else {
		return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
	}
}
复制代码

1.4 resttemplate->のhandleResponse()

デフォルトのコールチェーンにresttemplate的handleResponse、例外処理を含む、応答を処理する例外処理を呼び出すことができ、setErrorHandlerカスタムメソッドを設定しErrorHandler、異常判定処理及び要求に対する応答を実施しました。カスタマイズErrorHandler達成する必要ResponseErrorHandlerインターフェイスを、しかしSpring bootまた、デフォルトの実装を提供しDefaultResponseErrorHandler、継承によってそのクラスを達成することも可能ですErrorHandler

DefaultResponseErrorHandler40XのデフォルトBad Requestまたは50倍internal異常errorのエラー情報をキャプチャ。あなたは情報サービス自体によってスローされた例外をキャッチしたい場合、それはを通じて自己実現を必要としますRestTemplateErrorHandler

ResponseErrorHandler errorHandler = getErrorHandler();
               //判断响应是否有异常
	boolean hasError = errorHandler.hasError(response);
	if (logger.isDebugEnabled()) {
		try {
			int code = response.getRawStatusCode();
			HttpStatus status = HttpStatus.resolve(code);
			logger.debug("Response " + (status != null ? status : code));
		}catch (IOException ex) {
			// ignore
		}
	}
	//有异常进行异常处理
	if (hasError) {
		errorHandler.handleError(url, method, response);
	}
}
复制代码

1.5 HttpMessageConverterExtractor-> extractData()

デフォルトの呼び出しチェーンでは、HttpMessageConverterExtractorされてextractDataカプセル化するための応答メッセージとして行うjavaオブジェクトを、あなたが使用する必要があるmessageカスタムの方法を追加することにより、コンバータを増加させることができるmessageConverter。第一の従来の取得messageConverter、カスタム、その後messageConverter追加。

よれば、利用可能なソースコード、追加の実施形態を使用して、元の防止失われ、ソース。restTemplatesetMessageConvertersmessageConverter

public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        //检验
		validateConverters(messageConverters);
		// Take getMessageConverters() List as-is when passed in here
		if (this.messageConverters != messageConverters) {
		    //先清除原有的messageConverter
			this.messageConverters.clear();
			//后加载重新定义的messageConverter
			this.messageConverters.addAll(messageConverters);
		}
	}
复制代码

HttpMessageConverterExtractor的extractData出典:

MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
	if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
		return null;
	}
	//获取到response的ContentType类型
	MediaType contentType = getContentType(responseWrapper);

	try {
	    //依次循环messageConverter进行判断是否符合转换条件,进行转换java对象
		for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
		//会根据设置的返回类型responseType和contentType参数进行匹配,选择合适的MessageConverter
			if (messageConverter instanceof GenericHttpMessageConverter) {
				GenericHttpMessageConverter<?> genericMessageConverter =
						(GenericHttpMessageConverter<?>) messageConverter;
				if (genericMessageConverter.canRead(this.responseType, null, contentType)) {
					if (logger.isDebugEnabled()) {
						ResolvableType resolvableType = ResolvableType.forType(this.responseType);
						logger.debug("Reading to [" + resolvableType + "]");
					}
					return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
				}
			}
			if (this.responseClass != null) {
				if (messageConverter.canRead(this.responseClass, contentType)) {
					if (logger.isDebugEnabled()) {
						String className = this.responseClass.getName();
						logger.debug("Reading to [" + className + "] as \"" + contentType + "\"");
					}
					return (T) messageConverter.read((Class) this.responseClass, responseWrapper);
				}
			}
		}
	}
	.....
}
复制代码

contentTypeとmessageConverter 1.6との間の関係

に基づいて見られるプロセス、選択、メッセージ変換読み取り可能です。次のような関係は以下のとおりです。HttpMessageConverterExtractorextractDatacontentTyperesponseClassmessageConverter

クラス名 サポートされているのJavaType サポートされているのMediaType
ByteArrayHttpMessageConverter バイト[] アプリケーション/ octet-streamと、* / *
StringHttpMessageConverter text / plainで、* / *
ResourceHttpMessageConverter 資源 * / *
SourceHttpMessageConverter ソース / * + xmlのアプリケーション/ XML、text / xmlで、アプリケーション
AllEncompassingFormHttpMessageConverter 地図<K、リスト<?>> アプリケーション/ x-www-form-urlencodedで、マルチパート/フォームデータ
MappingJackson2HttpMessageConverter オブジェクト アプリケーション/ JSON、アプリケーション/ * + JSON
Jaxb2RootElementHttpMessageConverter オブジェクト / * + xmlのアプリケーション/ XML、text / xmlで、アプリケーション
JavaSerializationConverter Serializableを X-javaのシリアライゼーション;のcharset = UTF-8
FastJsonHttpMessageConverter オブジェクト * / *

2. springboot統合RestTemplate

  ソースコード解析の研究によれば、簡単に、簡単にRestTemplateに優雅なカスタム例外処理、追加など、プロジェクトで使用することができますMessageConverterし、インターセプタをinterceptor本明細書で使用する場合、例はdemo、以下のページを参照してください。

2.1輸入依存:(ウェブスタートに統合RestTemplate)

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <version>2.2.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.10</version>
  <scope>provided</scope>
</dependency>
复制代码

2.2. RestTemplat配置:

  • 使用ClientHttpRequestFactoryたとえば、プロパティRestTemplatの構成パラメータをConnectTimeoutReadTimeout
  • カスタムの追加interceptorインターセプタ、例外処理を、
  • 追加messageコンバータ;
  • カスタム例外ハンドラを設定します。

 @Configuration
public class RestTemplateConfig {

    @Value("${resttemplate.connection.timeout}")
    private int restTemplateConnectionTimeout;
    @Value("${resttemplate.read.timeout}")
    private int restTemplateReadTimeout;

    @Bean
    //@LoadBalanced
    public RestTemplate restTemplate( ClientHttpRequestFactory simleClientHttpRequestFactory) {
        RestTemplate restTemplate = new RestTemplate();
        //配置自定义的message转换器
        List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        messageConverters.add(new CustomMappingJackson2HttpMessageConverter());
        restTemplate.setMessageConverters(messageConverters);
        //配置自定义的interceptor拦截器
        List<ClientHttpRequestInterceptor> interceptors=new ArrayList<ClientHttpRequestInterceptor>();
        interceptors.add(new HeadClientHttpRequestInterceptor());
        interceptors.add(new TrackLogClientHttpRequestInterceptor());
        restTemplate.setInterceptors(interceptors);
        //配置自定义的异常处理
        restTemplate.setErrorHandler(new CustomResponseErrorHandler());
        restTemplate.setRequestFactory(simleClientHttpRequestFactory);

        return restTemplate;
    }

    @Bean
    public ClientHttpRequestFactory simleClientHttpRequestFactory(){
        SimpleClientHttpRequestFactory reqFactory= new SimpleClientHttpRequestFactory();
        reqFactory.setConnectTimeout(restTemplateConnectionTimeout);
        reqFactory.setReadTimeout(restTemplateReadTimeout);
        return reqFactory;
    }
}
复制代码

2.3。コンポーネント(カスタム例外ハンドラ、インターセプターインターセプター、メッセージコンバータ)

カスタムinterceptorインターセプタを達成するためClientHttpRequestInterceptorのインタフェースを

  • カスタムTrackLogClientHttpRequestInterceptorレコードresttemplaterequestresponse情報分析を追跡することができます。
  • カスタムHeadClientHttpRequestInterceptor、パラメータ設定要求ヘッダー。APIは、さまざまなリクエストを送信するために、多くの要求は、類似または同一のHTTPヘッダーを使用する必要があります。あなたがそれぞれの前にリクエストを考える場合はHeader塗りつぶしHttpEntity/RequestEntity、このようなコードは、むしろ冗長と思われるあなたは、統一されたインターセプタを設定することができます。

TrackLogClientHttpRequestInterceptor:

/**
 * @Auther: ccww
 * @Date: 2019/10/25 22:48,记录resttemplate访问信息
 * @Description:   记录resttemplate访问信息
 */
@Slf4j
public class TrackLogClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        trackRequest(request,body);
        ClientHttpResponse httpResponse = execution.execute(request, body);
        trackResponse(httpResponse);
        return httpResponse;
    }

    private void trackResponse(ClientHttpResponse httpResponse)throws IOException {
        log.info("============================response begin==========================================");
        log.info("Status code  : {}", httpResponse.getStatusCode());
        log.info("Status text  : {}", httpResponse.getStatusText());
        log.info("Headers      : {}", httpResponse.getHeaders());
        log.info("=======================response end=================================================");
    }

    private void trackRequest(HttpRequest request, byte[] body)throws UnsupportedEncodingException {
        log.info("======= request begin ========");
        log.info("uri : {}", request.getURI());
        log.info("method : {}", request.getMethod());
        log.info("headers : {}", request.getHeaders());
        log.info("request body : {}", new String(body, "UTF-8"));
        log.info("======= request end ========");
    }
}
复制代码

HeadClientHttpRequestInterceptor:

@Slf4j
public class HeadClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
    public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
       log.info("#####head handle########");
        HttpHeaders headers = httpRequest.getHeaders();
        headers.add("Accept", "application/json");
        headers.add("Accept-Encoding", "gzip");
        headers.add("Content-Encoding", "UTF-8");
        headers.add("Content-Type", "application/json; charset=UTF-8");
        ClientHttpResponse response = clientHttpRequestExecution.execute(httpRequest, bytes);
        HttpHeaders headersResponse = response.getHeaders();
        headersResponse.add("Accept", "application/json");
        return  response;
    }
}
复制代码

カスタム例外ハンドラ、継承DefaultResponseErrorHandlerまたは実装ResponseErrorHandlerインタフェース:

  • カスタム実装ErrorHandlerのアイデアは、応答メッセージの本文に応じて、対応する例外処理戦略、その他の異常の親であるDefaultResponseErrorHandler処理のために。
  • カスタムCustomResponseErrorHandler30X例外処理を行って

CustomResponseErrorHandler:

/**
 * @Auther: Ccww
 * @Date: 2019/10/28 17:00
 * @Description:  30X的异常处理
 */
@Slf4j
public class CustomResponseErrorHandler extends DefaultResponseErrorHandler {
    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        HttpStatus statusCode = response.getStatusCode();
        if(statusCode.is3xxRedirection()){
            return true;
        }
        return super.hasError(response);
    }

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        HttpStatus statusCode = response.getStatusCode();
        if(statusCode.is3xxRedirection()){
            log.info("########30X错误,需要重定向!##########");
            return;
        }
        super.handleError(response);
    }

}
复制代码

カスタムメッセージ改質器

/**
 * @Auther: Ccww
 * @Date: 2019/10/29 21:15
 * @Description: 将Content-Type:"text/html"转换为Map类型格式
 */
public class CustomMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
    public CustomMappingJackson2HttpMessageConverter() {
        List<MediaType> mediaTypes = new ArrayList<MediaType>();
        mediaTypes.add(MediaType.TEXT_PLAIN);
        mediaTypes.add(MediaType.TEXT_HTML);  //加入text/html类型的支持
        setSupportedMediaTypes(mediaTypes);// tag6
    }

}
复制代码

最後に、公共は、一緒になって、研究[番号] Ccwwノート心配であってもよいです。プラスグループ、毎日ビデオコレクションを学ぶだけでなく、乾燥品を共有することになります!

おすすめ

転載: juejin.im/post/5db99c285188257e435592ac