spring resttemplate 中文参数_简单粗暴的RestTemplate

前言通常访问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;@Aspect@Component@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调用封装 8edfd9e39d2dda67e2ad00c182f8aebf.png

        五、预告

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

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

56d5692884e6b2dc573edd5580d8cb0f.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值