欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

resttemplate get请求_简单粗暴的RestTemplate

程序员文章站 2022-06-24 21:38:06
...
前言通常访问http接口,我们有时候会使用httpclient,但是其代码复杂,还得费心进行各种资源回收的编写,不建议直接使用。而RestTemplate是Spring提供的用于访问Rest服务的客户端,对get,post等请求以及反序列化支持都封装的比较好,使用起来简单粗暴优雅。但是笔者在使用时候碰到一些问题:1、乱码。2、不同的请求需要设置不同的超时时间。 故整理出来与大家分享如何循序渐进的封装一个比较使用顺手、完善的配置类。 一、RestTemplate初步配置
import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.client.ClientHttpRequestFactory;import org.springframework.http.client.SimpleClientHttpRequestFactory;/** * RestTemplate配置类 */@Configurationpublic class RestTemplateConfig {    @Bean    public ClientHttpRequestFactory simpleClientHttpRequestFactory() {        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();        factory.setReadTimeout(5000);//读超时 单位为ms        factory.setConnectTimeout(5000);//连接超时 单位为ms        return factory;    }}
基于上面这点微量的配置代码,我们我们已经可以进行编写http请求代码了,粗暴简单吧! 二、使用初探下面来看下如何使用,他有post、get等spring封装好的API,也有稍微复杂点的exchange API, 但是我们的目的不是讨论这些API如何使用,而是建立一个完善已用省心的配置,这里就以post请求为例子。
import lombok.Data;import java.io.Serializable;@Datapublic class PersonRequest implements Serializable {    // 请求参数}import lombok.Data;import java.io.Serializable;@Datapublic class Person implements Serializable {    /**名称*/    private String name;    /**年龄*/    private int age;}public static void main(String[] args){        PersonRequest request= new PersonRequest ()        Person person= restTemplate.postForObject("http://172.27.249.130:3000/entity_service",                JSONObject.toJSONString(request), Person.class);
三、问题乱码配置如果只是上述简单配置,那么我们在服务端接受到的参数会出现中文乱码 {\"name\":\"??\",\"age\":18},这是因为我这里是以String格式提交数据的,底层其实采用的是StringHttpMessageConverter来处理请求,默认是ISO-8859-1字符集
@Beanpublic RestTemplate restTemplate(ClientHttpRequestFactory factory) {    RestTemplate restTemplate = new RestTemplate(factory);    // 解决字符串编码默认iso乱码问题,将字符集强制设置为UTF-8    List>> list = restTemplate.getMessageConverters();    for (HttpMessageConverter> httpMessageConverter : list) {        if (httpMessageConverter instanceof StringHttpMessageConverter) {            ((StringHttpMessageConverter) httpMessageConverter).setDefaultCharset(StandardCharsets.UTF_8);            break;        }    }    return restTemplate;}
这个时候我们的乱码就解决了 {\"name\":\"一斛墨水\",\"age\":18} 四、超时问题 我们的初步配置中,超时时间写死了,那么对于我们的url接口,可能A接口处理的慢,10秒钟才能返回,B接口1分钟才能返回,而C接口10毫秒就返回了,默认配置超时时间肯定不行,A和B接口只会触发超时。那有没有什么办法能够针对不同的接口设置不同的超时时间呢?
// 通过源码跟踪我们发现,// org.apache.http.impl.execchain.MainClientExec#execute// 在发起请求时会优先使用HttpClientContext中的超时时间设置@Overridepublic CloseableHttpResponse execute(        final HttpRoute route,        final HttpRequestWrapper request,        final HttpClientContext context,        final HttpExecutionAware execAware) throws IOException, HttpException {    ....            final int timeout = config.getSocketTimeout();            if (timeout >= 0) {                managedConn.setSocketTimeout(timeout);            }            ....}// 而config.getSocketTimeout()取的是RequestConfig属性socketTimeout的值// 那么我们就需要再使用的时候,每次重新设置这个值就可以了
五、超时配置
import org.apache.http.client.HttpClient;import org.apache.http.client.config.RequestConfig;import org.apache.http.client.protocol.HttpClientContext;import org.apache.http.protocol.HttpContext;import org.springframework.http.HttpMethod;import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;import java.net.URI;// 参考了https://blog.csdn.net/baozhutang/article/details/88664333public class RestmelateFactory extends HttpComponentsClientHttpRequestFactory {    // 每次请求都可以设置超时时间 单位毫秒 the timeout value in milliseconds        public static ThreadLocal socketTimeoutThreadLocal = new ThreadLocal<>();    public RestmelateFactory(HttpClient httpClient) {        super(httpClient);    }    @Override    protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {        HttpContext context = HttpClientContext.create();        RequestConfig config = createRequestConfig(getHttpClient());        // 每次都从新设置超时时间                // 从ThreadLocal中获取超时时间,并设置到context中                Integer socketTimeout = socketTimeoutThreadLocal.get();        if (null != socketTimeout) {            RequestConfig.Builder builder = RequestConfig.copy(config);            builder.setSocketTimeout(socketTimeout);            config = builder.build();        }        context.setAttribute(HttpClientContext.REQUEST_CONFIG, config);        return context;    }}@Beanpublic ClientHttpRequestFactory restTemplateFactory() {    HttpClientBuilder httpClientBuilder = HttpClients.custom();    // HttpClient相关配置          RestTemplateFactory factory = new RestTemplateFactory(httpClient);    factory.setConnectTimeout(5000);    // 即为 SocketTimeout        factory.setReadTimeout(30000);    factory.setConnectionRequestTimeout(5000);    return factory;}// 这次在使用,就可以根据不同的请求设置不同的超时时间了// 可以再url服务端打上断点,一直等到restTemplate超时,// 会发现日志前后调用耗时就是你得超时时间public static void main(String[] args) {    try {       // 设置当前请求超时时间                RestTemplateFactory.socketTimeoutThreadLocal.set(10);        PersonRequest request = new PersonRequest() log.info("handler, start request:" + request);        try {            Person person = restTemplate.postForObject("http://172.27.249.130:3000/entity_service", JSONObject.toJSONString(request), Person.class);        } catch (RestClientException e) {            log.error("handler接口异常, request:" + request, e);        }    } finally {        // 清理ThreadLocal中超时时间                RestTemplateFactory.socketTimeoutThreadLocal.remove();    }}
六、切面设置超时时间每次调用都要编写重复代码设置超时时间到ThreadLocal,我强迫症,不能忍。大家要是能忍的话就可以不用再往下看了。下面我们就使用注解加切面消除这项重复工作。
import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * RestTemplate配置,比如目前只有的超时 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface RestTemplateAnnotation {    /**请求超时 the timeout value in milliseconds*/    int timeout();}import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;@[email protected]@Slf4jpublic class RestTemplateAspect {    @Pointcut(value = "@annotation(com.annotation.RestTemplateAnnotation)")    public void accessOperation() {    }    @Around(value = "accessOperation() && @annotation(restTemplateAnnotation)")    public Object doAround(ProceedingJoinPoint joinPoint, RestTemplateAnnotation restTemplateAnnotation) throws Throwable {        try {            // 设置当前请求超时时间            HttpClientRequestFactory.socketTimeoutThreadLocal.set(restTemplateAnnotation.timeout());            return joinPoint.proceed();        } finally {            // 清理ThreadLocal中超时时间            HttpClientRequestFactory.socketTimeoutThreadLocal.remove();        }    }}/** * 注解使用 * * @param request * @return  */@RestTemplateAnnotation(timeout = 60000) public void handler () {  PersonRequest request = new PersonRequest() log.info("handler, start request:" + request);  try {      Person person = restTemplate.postForObject("http://172.27.249.130:3000/entity_service", JSONObject.toJSONString(request), Person.class);  } catch (RestClientException e) {      log.error("handler接口异常, request:" + request, e);  }}
至此我们就完成了一个使用简单粗暴优雅的http调用封装 resttemplate get请求_简单粗暴的RestTemplate

        五、预告

        下期 :让电脑桌面烦人的羞羞广告弹窗无处遁形

如果此文对您有所帮助,烦请关注一下,以方便之后的文章能更及时、更好的帮助到您,谢谢!

resttemplate get请求_简单粗暴的RestTemplate