• RestTemplate (二) : RestOperations、具体API使用、RestTemplate原理介绍、使用案例



    RestTemplate源码详解深入剖析底层实现思路 (优先)

    一、RestOperations

    指定一组基本restful操作的接口,定义了基本的Rest操作集合,它的唯一实现是RestTemplate

    在这里插入图片描述

    该接口中的方法分类

    • getForObject / getForEntity 分为一组,这类方法是常规的 Rest API(GET、POST、DELETE 等)方法调用
    • exchange:接收一个 RequestEntity 参数,可以自己设置 HTTP method,URL,headers 和 body,返回 ResponseEntity
    • execute:通过 callback 接口,可以对请求和返回做更加全面的自定义控制
    HttpMethod方法
    GET在这里插入图片描述
    POST在这里插入图片描述
    PUT在这里插入图片描述
    DELETE在这里插入图片描述
    HEAD在这里插入图片描述
    PATCH在这里插入图片描述
    OPTIONS在这里插入图片描述
    通用 exchange / execute 指定HTTP方法在这里插入图片描述

    每个方法都有三种重载实现:2种的url参数为字符串,1种URI为参数

    • 使用字符串类型的url默认会对url进行转义, 推荐使用 URI uri = uriComponents.toUri()来构造

    xxxForObject:返回响应体(也就直接是body的内容) (T)
    xxxForEntity:返回的相应行、响应头、响应码、响应体等 (ResponseEntity)
    xxxForLocation:提交成功之后,返回新资源的URI。这个只需要服务提供者返回一个 URI 即可,该 URI 表示新资源的位置,可谓非常轻量。 (URI)

    1.1 API的详细使用

    RestTemplate中API详细使用介绍

    1.2 exchange和execute方法

    @Override
    public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType)
    		throws RestClientException {
    
    	RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
    	ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
    	return nonNull(doExecute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor));
    }
    
    @Override
    public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
    		ParameterizedTypeReference<T> responseType) throws RestClientException {
    
    	Type type = responseType.getType();
    	RequestCallback requestCallback = httpEntityCallback(requestEntity, type);
    	ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
    	return nonNull(execute(url, method, requestCallback, responseExtractor));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    RequestCallback:用于操作请求头和body,在请求发出前执行;

    • RequestCallback.doWithRequest(ClientHttpRequest)就是拿到ClientHttpRequest后对它进行处理
    • RestTemplate的acceptHeaderRequestCallback、httpEntityCallback这些方法可以设置

    ResponseExtractor:解析/提取HTTP响应的数据,而且不需要担心异常和资源的关闭

    二、POST请求的方式

    POST请求它传参是有两种方式:

    1. Form Data:from表单提交请求就是这种方式;使用ajax请求也是这种方式; application/x-www-form-urlencoded
      在这里插入图片描述
    2. request payload : application/json
      在这里插入图片描述
      在这里插入图片描述

    这两种方式是通过Content-Type来区别的:

    • 若是application/x-www-form-urlencoded那就是formdata方式;
    • 若是application/json或者multipart/form-data(表单上传文件)等方式, 那就是request payload方式

    三、HttpAccessor

    HttpAccessor是个抽象基类,它定义要操作ClientHttpRequestFactory的公共属性,它一般不直接使用

    // @since 3.0
    public abstract class HttpAccessor {
    	
    	// RestTemplate默认使用的客户端工厂:基于源生JDK
    	private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
    
    	// 若要切换成三方库的底层组件,设置此方法便可
    	public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
    		this.requestFactory = requestFactory;
    	}
    	... // get方法
    	
    	// 供给子类非常方便的拿到一个ClientHttpRequest
    	protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
    		ClientHttpRequest request = getRequestFactory().createRequest(url, method);
    		return request;
    	}	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    它的子类是:InterceptingHttpAccessor,也还是个抽象实现,主要是管理起了请求的拦截器们:ClientHttpRequestInterceptor。

    在这里插入图片描述
    在这里插入图片描述

    4.1 InterceptingHttpAccessor

    // @since 3.0
    // @see InterceptingClientHttpRequestFactory
    public abstract class InterceptingHttpAccessor extends HttpAccessor {
    
    	// 装载需要作用在RestTemplate上的拦截器
    	private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
    	@Nullable
    	private volatile ClientHttpRequestFactory interceptingRequestFactory;
    
    	// 这里语意是set,所以是完全的替换掉(支持ordered排序)
    	public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
    		if (this.interceptors != interceptors) {
    			this.interceptors.clear();
    			this.interceptors.addAll(interceptors);
    			AnnotationAwareOrderComparator.sort(this.interceptors);
    		}
    	}
    
    	// 复写了父类的这个方法很有意思
    	// 意思为:若你调用者手动set进来了,那就以调用者设置的工厂为准 否则使用的是InterceptingClientHttpRequestFactory
    	@Override
    	public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
    		super.setRequestFactory(requestFactory);
    		this.interceptingRequestFactory = null;
    	}
    
    	// 若配置了拦截器,那么默认就使用InterceptingClientHttpRequestFactory,而不再是SimpleClientHttpRequestFactory了
    	@Override
    	public ClientHttpRequestFactory getRequestFactory() {
    		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();
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    InterceptingHttpAccessor最主要的处理逻辑为:若发现调用者设置了请求拦截器,那么它创建的工厂是具有拦截功能的InterceptingClientHttpRequestFactory,否则就是默认的SimpleClientHttpRequestFactory

    InterceptingClientHttpRequestFactory工厂它产生的ClientHttpRequest是InterceptingClientHttpRequest,然而它就会执行拦截器的拦截方法喽:nextInterceptor.intercept(request, body, this)

    四、RestTemplate

    • RestTemplate是Spring提供的用于访问Rest服务的客户端工具,它提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。

    • RestTemplate简化了与http服务的通信,程序代码可以给它提供URL,并提取结果。它默认使用的JDK 的HttpURLConnection进行通信,然而我们是可以通过RestTemplate.setRequestFactory切换到不同的HTTP源:如Apache HttpComponents、Netty、OkHttp等等。

    • 它实现了接口RestOperations, 提供了非常多的模版方法(重载方法)让开发者能更简单地发送 HTTP 请求

    下面都是在对构建一个RestTemplate实例的准备工作相关方法,包括对各个相关组件的设置

    // @since 3.0
    public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
    	// 去classpath探测  是否有这些消息转换器相关的jar
    	// 一般情况下我们都会导jackson2Present
    	private static boolean romePresent;
    	private static final boolean jaxb2Present;
    	private static final boolean jackson2Present;
    	private static final boolean jackson2XmlPresent;
    	private static final boolean jackson2SmilePresent;
    	private static final boolean jackson2CborPresent;
    	private static final boolean gsonPresent;
    	private static final boolean jsonbPresent;
    	...
    	
    	// 下面四个变量很重要:
    
    	// 消息转换器们(显然对JSON格式默认是支持得最好的)
    	private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
    	// 默认的请求异常处理器,Spring5.0后其实可以使用它ExtractingResponseErrorHandler
    	// 它能够利用消息转换器提取你的错误内容。并且还支持自定义错误码、错误序列等等
    	private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();
    	// 用于URL的构建
    	private UriTemplateHandler uriTemplateHandler;
    	// 默认的返回值提取器
    	private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor();
    
    	// 空构造,应该是平时使用得最多的了:一切都使用默认的组件配置Resource等等
    	public RestTemplate() {
    		// 这个几个消息转换器是支持的。字节数组、字符串、
    		this.messageConverters.add(new ByteArrayHttpMessageConverter());
    		this.messageConverters.add(new StringHttpMessageConverter());
    		this.messageConverters.add(new ResourceHttpMessageConverter(false));
    		this.messageConverters.add(new SourceHttpMessageConverter<>());
    		// 对form表单提交方式的支持
    		this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
    
    		// 接下里便是一些列的判断,若类路径上有才会加进来
    		if (jackson2Present) {
    			this.messageConverters.add(new MappingJackson2HttpMessageConverter());
    		}
    		...
    		// new DefaultUriBuilderFactory()
    		this.uriTemplateHandler = initUriTemplateHandler();
    	}
    
    	// 若想用OkHttp,也可以在构造时就指定
    	public RestTemplate(ClientHttpRequestFactory requestFactory) {
    		this();
    		setRequestFactory(requestFactory);
    	}
    
    	// 若不想用默认的消息转换器,也可以自己指定(其实一般都不这么去干,而是后面自己再add进来)
    	public RestTemplate(List<HttpMessageConverter<?>> messageConverters) {
    		Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required");
    		this.messageConverters.addAll(messageConverters);
    		this.uriTemplateHandler = initUriTemplateHandler();
    	}
    	... // 省略上面属性的get/set犯法们
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    接下来更重要的便是它实现的接口方法了

    RestTemplate@Override
    	@Nullable
    	public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
    		// new AcceptHeaderRequestCallback(responseType)  在发送请求的之前做这样一件事:
    		// request.getHeaders().setAccept(allSupportedMediaTypes)
    		RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
    		HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
    
    		// 最终调用的是execute方法,此时URL是个字符串
    		// responseExtractor返回值提取器 使用的是消息转换器去读取body
    		// 返回值就是返回的body本身(不含有返回的响应头等等信息)
    		return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
    	}
    
    	// 它返回的是ResponseEntity,不会返回null的  最终调用的依旧是execute方法
    	// 此时用的就不是消息转换器的提取器了,而是内部类`ResponseEntityResponseExtractor`(底层还是依赖消息转换器)
    	// 但是这个提取器,提取的都是ResponseEntity实例
    	@Override
    	public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
    		RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
    		ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
    		return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));
    	}
    
    	// HEAD请求:很简单,使用的提取器就是headersExtractor,从返回值里把响应header拿出来即可
    	@Override
    	public HttpHeaders headForHeaders(String url, Object... uriVariables) throws RestClientException {
    		return nonNull(execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables));
    	}
    
    
    	// POST请求
    	@Override
    	@Nullable
    	public URI postForLocation(String url, @Nullable Object request, Object... uriVariables) throws RestClientException {
    		// HttpEntityRequestCallback  适配:把request适配成一个HttpEntity
    		// 然后执行前,通过消息转换器把头信息、body信息等等都write进去
    		RequestCallback requestCallback = httpEntityCallback(request);
    		// 因为需要拿到URI,所以此处使用headersExtractor提取器先拿到响应的header即可
    		HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables);
    		return (headers != null ? headers.getLocation() : null);
    	}
    
    	// 除了httpEntityCallback()不一样,其余和get请求一样
    	@Override
    	@Nullable
    	public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables) throws RestClientException {
    		RequestCallback requestCallback = httpEntityCallback(request, responseType);
    		HttpMessageConverterExtractor<T> responseExtractor =
    				new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
    		return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
    	}
    
    	// PUT请求:因为没有返回值,所以不需要返回值提取器。
    	@Override
    	public void put(String url, @Nullable Object request, Object... uriVariables) throws RestClientException {
    		RequestCallback requestCallback = httpEntityCallback(request);
    		execute(url, HttpMethod.PUT, requestCallback, null, uriVariables);
    	}
    
    	// DELETE请求:也是没有返回值的。
    	// 并且请注意:DELETE请求都是不能接收body的,不能给请求设置请求体的
    	// (虽然可能底层httpCLient支持,但这里不支持,请遵守规范)
    	@Override
    	public void delete(String url, Object... uriVariables) throws RestClientException {
    		execute(url, HttpMethod.DELETE, null, null, uriVariables);
    	}
    
    	// OPTIONS请求:和HEAD请求的处理逻辑几乎一样
    	@Override
    	public Set<HttpMethod> optionsForAllow(String url, Object... uriVariables) throws RestClientException {
    		ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
    		HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables);
    		return (headers != null ? headers.getAllow() : Collections.emptySet());
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    所有方法大体执行逻辑一致,都是和RequestCallbackresponseExtractor等有关,且最终都是委托给了最为底层的execute()方法去执行。

    exchange():

    exchange所有方法使用的都是HttpEntityResponseEntity代表请求实体和响应实体

    RestTemplate@Override
    	public <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
    		// 把请求体适配为HttpEntity
    		RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
    		// 消息提取器使用ResponseEntityResponseExtractor
    		ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
    
    		// 从上两个部分就能看到:exchange方法的入参、出参都是非常通用的
    		return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables));
    	}
    
    	// ParameterizedTypeReference参数化类型,用于处理泛型
    	// 上面的responseType就是个Class。这里是个参数化类型
    	@Override
    	public <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException {
    
    		Type type = responseType.getType();
    		RequestCallback requestCallback = httpEntityCallback(requestEntity, type);
    		ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
    		return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables));
    	}
    
    	// 这个方法就非常精简了,让调用者自己去构造RequestEntity,里面是包含了请求的URL和方法等信息的
    	@Override
    	public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType) throws RestClientException {
    		RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
    		ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
    		return nonNull(doExecute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor));
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    在Spring3.2后提供了ParameterizedTypeReference来处理参数化类型—> 主要是为了处理List等的泛型

    可以发现即使是exchange()方法,最终还是委托给execute/doExecute去执行的:

    RestTemplate// 3个execute方法。最终调用的都是doExecute方法
    	// 它做的一件事:使用UriTemplateHandler把URL的参数填进去
    	// 底层使用的是我上文介绍的`UriComponentsBuilder`,还是比较简单的
    	@Override
    	@Nullable
    	public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
    		URI expanded = getUriTemplateHandler().expand(url, uriVariables);
    		return doExecute(expanded, method, requestCallback, responseExtractor);
    	}
    
    doExecute方法:
    	@Nullable
    	protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
    		ClientHttpResponse response = null;
    		ClientHttpRequest request = createRequest(url, method);
    		// 如果有回调,那就先回调处理一下子请求
    		if (requestCallback != null) {
    			requestCallback.doWithRequest(request);
    		}
    		// 真正意义上的发送请求。
    		// 请注意:如果这里的request是`InterceptingClientHttpRequest`,那就回执行拦截器的intercept方法
    		// 至于什么时候是InterceptingClientHttpRequest呢?这个上面有讲的
    		response = request.execute();
    		// 处理结果(若有错误,那就抛出异常)
    		handleResponse(url, method, response);
    		
    		// 请求正常。那就使用返回值提取器responseExtractor提取出内容即可
    		return (responseExtractor != null ? responseExtractor.extractData(response) : null);
    		...
    		// 关闭响应(ClientHttpResponse继承了Closeable接口)
    		finally {
    			if (response != null) {
    				response.close();
    			}
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    看完doExecute()的模板式的实现步骤,就清楚了RestTemplate从发出一个请求到收到一个响应的完整过程。

    4.1 创建RestTemplate

    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        RestTemplate restTemplate = new RestTemplate(factory);
        return restTemplate;
    }
    
    @Bean
    public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setReadTimeout(5000);
        factory.setConnectTimeout(15000);
        // 设置代理
        //factory.setProxy(null);
        return factory;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    创建 RestTemplate 时需要一个 ClientHttpRequestFactory,通过这个请求工厂,我们可以统一设置请求的超时时间,设置代理以及一些其他细节。通过上面代码配置后,我们直接在代码中注入 RestTemplate 就可以使用了。

    4.2 普通接口调用

    Map<String, String> vars = Collections.singletonMap("hotel", "42");
    // 通过 GET 方式调用,返回一个 String 值,还可以给 URL 变量设置值(也可通过 uriTemplateHandler 这个属性自定义)
    String result = restTemplate.getForObject(
            "https://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
    
    // -------------------------------------------
    
    String url = "http://127.0.0.1:8080/hello";
    JSONObject param = new JSONObject();
    //restTemplate 会根据 params 的具体类型,调用合适的 HttpMessageConvert 将请求参数写到请求体 body 中,并在请求头中添加合适的 content-type;
    // 也会根据 responseType 的类型(本列子中是 JSONObject),设置 head 中的 accept 字段,当响应返回的时候再调用合适的 HttpMessageConvert 进行响应转换
    ResponseEntity<JSONObject> responseEntity=restTemplate.postForEntity(url,params,JSONObject.class);
    int statusCodeValue = responseEntity.getStatusCodeValue();
    HttpHeaders headers = responseEntity.getHeaders();
    JSONObject body = responseEntity.getBody();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4.3 添加 Header 和 Cookie

    有时候,我们需要在请求中的 Head 中添加值或者将某些值通过 cookie 传给服务端

    UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl("127.0.0.1:8080").
                    path("/test").build(true);
     URI uri = uriComponents.toUri();
    
    RequestEntity<JSONObject> requestEntity = RequestEntity.post(uri).
                    // 添加 cookie(这边有个问题,假如我们要设置 cookie 的生命周期,作用域等参数我们要怎么操作)
                    header(HttpHeaders.COOKIE,"key1=value1").
                    // 添加 header
                    header(("MyRequestHeader", "MyValue")
                    accept(MediaType.APPLICATION_JSON).
                    contentType(MediaType.APPLICATION_JSON).
                    body(requestParam);
    ResponseEntity<JSONObject> responseEntity = restTemplate.exchange(requestEntity,JSONObject.class);
    // 响应结果
    JSONObject responseEntityBody = responseEntity.getBody();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4.4 请求拦截器、ErrorHandler 配置

    4.4.1 拦截器配置 (ClientHttpRequestInterceptor)

    RestTemplate 也可以设置拦截器做一些统一处理。这个功能感觉和 Spring MVC 的拦截器类似。配置也很简单:

    class MyInterceptor implements ClientHttpRequestInterceptor{
    
         @Override
         public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
             logger.info("enter interceptor...");
             return execution.execute(request,body);
         }
    }
        
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        RestTemplate restTemplate = new RestTemplate(factory);
        MyInterceptor myInterceptor = new MyInterceptor();
        List<ClientHttpRequestInterceptor> list = new ArrayList<>();
        list.add(myInterceptor);
        restTemplate.setInterceptors(list);
        return restTemplate;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    4.4.2 ErrorHandler (DefaultResponseErrorHandler)

    ErrorHandler 用来对调用错误对统一处理。

    public class MyResponseErrorHandler extends DefaultResponseErrorHandler {
    
            @Override
            public boolean hasError(ClientHttpResponse response) throws IOException {
                return super.hasError(response);
            }
    
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
                if (statusCode == null) {
                    throw new UnknownHttpStatusCodeException(response.getRawStatusCode(), response.getStatusText(),
                            response.getHeaders(), getResponseBody(response), getCharset(response));
                }
                handleError(response, statusCode);
            }
            @Override
            protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
                switch (statusCode.series()) {
                    case CLIENT_ERROR:
                        HttpClientErrorException exp1 = new HttpClientErrorException(statusCode, response.getStatusText(), response.getHeaders(), getResponseBody(response), getCharset(response));
                        logger.error("客户端调用异常",exp1);
                        throw  exp1;
                    case SERVER_ERROR:
                        HttpServerErrorException exp2 = new HttpServerErrorException(statusCode, response.getStatusText(),
                                response.getHeaders(), getResponseBody(response), getCharset(response));
                        logger.error("服务端调用异常",exp2);
                        throw exp2;
                    default:
                        UnknownHttpStatusCodeException exp3 = new UnknownHttpStatusCodeException(statusCode.value(), response.getStatusText(),
                                response.getHeaders(), getResponseBody(response), getCharset(response));
                        logger.error("网络调用未知异常");
                        throw exp3;
                }
            }
    }
    
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        RestTemplate restTemplate = new RestTemplate(factory);
        MyResponseErrorHandler errorHandler = new MyResponseErrorHandler();
        restTemplate.setErrorHandler(errorHandler);
        List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        // 通过下面代码可以添加新的 HttpMessageConverter
        //messageConverters.add(new );
        return restTemplate;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    4.4.3 HttpMessageConverter

    RestTemplate 也可以配置 HttpMessageConverter,配置的原理和 Spring MVC 中类似。

    4.5 使用案例

    public class AbstractHelper {
        private static final HLogger logger = HLoggerFactory.getLogger(AbstractHelper.class);
        @Autowired
        private RestTemplate restTemplate;
    
        protected <T> ResponseEntity<T> execute(HttpMethod httpMethod, String domain, String path, @Nullable Map<String, Object> params,
                                                @Nullable Map<String, String> headers, @Nullable Object body, Class<T> tClass) {
            HttpHeaders httpHeaders = null;
            if (headers != null) {
                httpHeaders = new HttpHeaders();
                httpHeaders.setContentType(MediaType.APPLICATION_JSON);
                for (Map.Entry<String, String> next : headers.entrySet()) {
                    httpHeaders.add(next.getKey(), next.getValue());
                }
            }
            HttpEntity<Object> requestEntity = new HttpEntity<>(body, httpHeaders);
            URI uri = buildURI(domain, path, params);
            logger.info("AbstractHelper execute:{}", uri);
            ResponseEntity<T> exchange;
            if (tClass == String.class || ClassUtils.isPrimitiveOrWrapper(tClass)) {
                exchange = this.restTemplate.exchange(uri, httpMethod, requestEntity, tClass);
            } else {
                exchange = this.restTemplate.exchange(uri, httpMethod, requestEntity, new ParameterizedTypeReference<T>() {
                });
            }
            return exchange;
        }
    
        protected URI buildURI(String domain, String path, Map<String, Object> params) {
            UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(domain)
                    .path(path);
            if (MapUtils.isNotEmpty(params)) {
                params.forEach((key, value) -> builder.queryParam(key, value));
            }
            return builder.build(true).toUri();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    @Slf4j
    @Component
    public class HkeContainerHelper extends AbstractHelper {
    
        private static final String ENV = EnvUtil.ENV;
        /**
         * 业务线接口
         */
        private static String pod_detail = "/container-business-service/open-api/v3/app/query/pods";
    
    
        public PodResp getPodDetail(String appId, String podOrIp) {
            HashMap<String, Object> params = Maps.newHashMap();
            params.put("pageSize", 1);
            params.put("statusList", Arrays.asList("running"));
            params.put("appNameList", Arrays.asList(appId));
            params.put("envCodeList", Arrays.asList(ENV.toUpperCase()));
            if (podOrIp.contains("-")) {
                String podName = podOrIp.startsWith(appId.toLowerCase()) ?
                        podOrIp : String.format("%s-%s", appId.toLowerCase(), podOrIp);
                params.put("podNameList", Arrays.asList(podName));
            } else if (podOrIp.contains(".")) {
                params.put("ipAddressList", Arrays.asList(podOrIp));
            } else {
                return null;
            }
            String result = this.executeForRawByPost(pod_detail, params);
            JSONObject data = JSON.parseObject(result);
            if (data != null) {
                JSONArray records = data.getJSONObject("data").getJSONArray("podList");
                if (records != null && !records.isEmpty()) {
                    JSONObject record = (JSONObject) records.get(0);
                    return PodResp.builder().podName(record.getString("podName")).group(record.getString("groupName"))
                            .deployResource(record.getString("deployResource"))
                            .containers(record.getString("containersNameList")).build();
                }
            }
            return null;
        }
    
    
        public List<MachineEntity> getPodList(String appId, List<String> podNameList) {
            if (CollectionUtils.isEmpty(podNameList)) {
                return Collections.emptyList();
            }
            podNameList = podNameList.stream().map(t -> {
                return String.format("%s-%s", appId.toLowerCase(), t);
            }).collect(Collectors.toList());
            HashMap<String, Object> params = Maps.newHashMap();
            params.put("podNameList", podNameList);
            params.put("pageSize", podNameList.size());
            String result = this.executeForRawByPost(pod_detail, params);
            JSONObject data = JSON.parseObject(result);
            if (data != null) {
                JSONArray records = data.getJSONObject("data").getJSONArray("podList");
                if (records != null && !records.isEmpty()) {
                    return records.stream()
                            .map(t -> {
                                JSONObject record = (JSONObject) t;
                                String name = record.getString("podName");
                                if (!StringUtils.isBlank(name)) {
                                    name = name.substring(name.indexOf("-") + 1);
                                }
                                String deployModel = record.getString("deployModel");
                                MachineEntity machine = MachineEntity.builder()
                                        .ip(record.getString("ipAddress"))
                                        .name(name)
                                        .unit(record.getString("appUnitCode"))
                                        .unitFlag(!StringUtils.isBlank(deployModel) && deployModel.equals("UNIT") ? true : false)
                                        .build();
                                return machine;
                            }).collect(Collectors.toList());
                }
            }
            return Collections.emptyList();
        }
    
        private String executeForRaw(String apiPath, Map<String, Object> params) {
            HkeConfig config = ApplicationConfig.getHkeConfig();
            params = params == null ? new HashMap<>(2) : params;
            params.put("X-ACCESS-TOKEN", "api:" + config.getToken());
            return this.execute(
                    HttpMethod.GET,
                    config.getUrl(),
                    apiPath,
                    params,
                    null,
                    null,
                    String.class
            ).getBody();
        }
    
        private String executeForRawByPost(String apiPath, Map<String, Object> body) {
            HkeConfig config = ApplicationConfig.getHkeConfig();
            Map<String, String> headers = new HashMap<>();
            headers.put("X-ACCESS-TOKEN", "api:" + config.getToken());
            return this.execute(
                    HttpMethod.POST,
                    config.getUrl(),
                    apiPath,
                    null,
                    headers,
                    body,
                    String.class
            ).getBody();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
  • 相关阅读:
    Netty P1 NIO 基础,网络编程
    再有人说技术人不懂浪漫,就把这篇文章甩他脸上
    JAVA 0基础 字符编码
    案例分享-full gc导致k8s pod重启
    简单聊下STM32F103的串口
    计算属性与watch的区别,fetch与axios在vue中的异步请求,单文本组件使用,使用vite创建vue项目,组件的使用方法
    别再用Mybatis Plus 的伪批量新增了!
    selenium工作原理和反爬分析
    2022年十月份钓鱼威胁情报
    vue.js el-tooltip根据文字长度控制是否提示toolTip
  • 原文地址:https://blog.csdn.net/m0_37989980/article/details/126507644