Retrofit2 使用说明(Token处理进阶)

一、 概述

Retrofit2Http接口调用框架(对httpclient的封装基于OkHttp3),调用http接口就像调用本地接口一样简单。

官网教程:https://square.github.io/retrofit/

2.0 版本改进参考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0915/3460.html

对比过RestTemplate,再看Retrofit2的使用,后者有多简便易用,看后一目了然。

直抒主题:

二、Maven

 Retrofit与spring-boot框架直接整合

        <dependency>
            <groupId>com.squareup.retrofit2</groupId>
            <artifactId>retrofit</artifactId>
            <version>2.9.0</version>
        </dependency>

三、配置

配置文件如下(详细参考注释):
# ===== retrofit ===== #
retrofit:
  # 连接池配置
  pool:
    # testpool连接池配置
    testpool:
      # 最大空闲连接数
      max-idle-connections: 3
      # 连接保活时间(秒)
      keep-alive-second: 100
  # 是否禁用void返回值类型
  disable-void-return-type: false
  # 全局转换器工厂
  global-converter-factories:
    - retrofit2.converter.jackson.JacksonConverterFactory
  # 全局调用适配器工厂
  global-call-adapter-factories:
    - com.github.lianjiatech.retrofit.spring.boot.core.BodyCallAdapterFactory
    - com.github.lianjiatech.retrofit.spring.boot.core.ResponseCallAdapterFactory
  # 日志打印配置
  log:
    # 启用日志打印
    enable: true
    # 日志打印拦截器
    logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor
    # 全局日志打印级别
    global-log-level: info
    # 全局日志打印策略
    global-log-strategy: body
  # 重试配置
  retry:
    # 是否启用全局重试
    enable-global-retry: true
    # 全局重试间隔时间
    global-interval-ms: 100
    # 全局最大重试次数
    global-max-retries: 1
    # 全局重试规则
    global-retry-rules:
      - response_status_not_2xx
      - occur_io_exception
    # 重试拦截器
    retry-interceptor: com.github.lianjiatech.retrofit.spring.boot.retry.DefaultRetryInterceptor
  # 熔断降级配置
  degrade:
    # 是否启用熔断降级
    enable: false
    # 熔断降级实现方式
    degrade-type: sentinel
    # 熔断资源名称解析器
    resource-name-parser: com.github.lianjiatech.retrofit.spring.boot.degrade.DefaultResourceNameParser

四、实例代码

编写请求接口:

@RetrofitClient(baseUrl = "${baseUrl}", poolName = "testpool", errorDecoder = DingErrorDecoder.class)
public interface DHttpApi {

    /**
     * @param appkey
     * @param appsecret
     * @return
     */
    @GET("gettoken")
    Result gettoken(@Query("appkey") String appkey, @Query("appsecret") String appsecret);

}
#配置文件url路径
baseUrl:
    https://api.xxx.com

调用方式,与普通接口调用相同:

    @Autowired
    private DHttpApi dHttpApi;

    public Result gettoken() {
        return dHttpApi.gettoken(appkey, appsecret);
    }

以上配置结束,已经可以使用了,是不是超简单,配置无侵入、全注释即可处理。

 

五、进阶(Token处理)

1. 自动在每次请求加入公共参数(Token)

为了避免每次请求都写Token,统一在此处处理,

接口加入拦截器配置:

@Intercept(handler = DTokenInterceptor.class)
public interface DingHttpApi {

拦截器获取token,代码如下:

@Component
public class DTokenInterceptor extends BasePathMatchInterceptor {
    @Autowired
    private Environment env;

    @Override
    public Response doIntercept(Chain chain) throws IOException {
        String token = DTokenKeeper.getAccessToken();
        // 初始获得token
        if (null == token || token.equals("")) {
            try {
                Response resp = getToken(env.getProperty("DING.baseUrl") + "/gettoken?appkey="
                        + env.getProperty("DING.appkey") + "&appsecret=" + env.getProperty("DING.appsecret"));
                JSONObject jObj = JSON.parseObject(resp.body().string());
                token = jObj.getString(DingConstant.TOKEN);
                DingTokenKeeper.setAccessToken(token);
            } catch (Exception e) {
                throw new AppException("获取token失败!");
            }
        }
        Request request = chain.request();
        Request newRequest;
        HttpUrl url = request.url();
        // 参数加入token
        HttpUrl newUrl = url.newBuilder()
                .addQueryParameter(DingConstant.TOKEN, token)
                .build();
        newRequest = request.newBuilder()
                .url(newUrl)
                .build();
        return chain.proceed(newRequest);
    }

}

坑点:

获取token也需要进行http请求,此处不能直接使用retrofit2

因为retrofit2框架注册晚于拦截器,所以此处请求要借助原生okhttp3来完成....

    /**
     * 原生http请求
     */
    private Response getToken(String url) throws Exception {
        //1 构造Request
        OkHttpClient okHttpClient = new OkHttpClient();
        Request.Builder builder = new Request.Builder();
        Request request = builder.get().url(url).build();
        //2 将Request封装为Call
        Call call = okHttpClient.newCall(request);
        //3 执行Call,得到response
        return call.execute();
    }

2. Token过期刷新

处理方式为记录token生成时间,请求时与有效期对比,过期则重新获取,此方式较简单,参考如下

// 在拦截器中加入判断即可
if (System.currentTimeMillis() - DingTokenKeeper.getTokenTime().longValue()) > DingConstant.REFRESH_TOKEN_VALID_TIME) {
// TODO 获取token
}
public class DingTokenKeeper {

    // 保证原子性
    private static AtomicLong tokenTime = new AtomicLong(0L);
    /**
     * dingding accessToken
     */
    private static String accessToken = "";

    public static void setAccessToken(String accessToken) {
        log.info("Update token ---------> " + accessToken);
        DingTokenKeeper.setTokenTime(new AtomicLong(System.currentTimeMillis()));
        DingTokenKeeper.accessToken = accessToken;
    }

    public static String getAccessToken() {
        return accessToken;
    }

    public static AtomicLong getTokenTime() {
        return tokenTime;
    }

    public static void setTokenTime(AtomicLong tokenTime) {
        DingTokenKeeper.tokenTime = tokenTime;
    }
}

3. 自动拦截返回错误,统一处理异常,如token失效等

拦截器代码,错误统一处理:

@Component
public class DingErrorDecoder implements ErrorDecoder {
    @Override
    public RuntimeException invalidRespDecode(Request request, Response response) {
        try {
            String responseBody = RetrofitUtils.readResponseBody(response);
            if (!response.isSuccessful()) {
                throw new AppException("非法响应,request=" + request + "  response=" + response + "body=" + responseBody);
            } else {
                JSONObject jObj = JSON.parseObject(responseBody);
                // 错误处理
                if (StringUtils.isNotBlank(jObj.getString(DingConstant.ERROR_CODE))) {
                    throw new AppException(responseBody);
                }
            }
        } catch (ReadResponseBodyException e) {
            throw new AppException(e.getMessage());
        }
        return null;
    }
}

以上代码 即可应付多数HTTP请求,适用于微服务架构。

Token处理方式因项目而异,所谓条条大路通罗马,只要简单,实用,就可以释放生产力,创造更多价值,在此为这些开源项目点赞!

更高进阶,后续关注。


喜欢的朋友请点赞,多谢支持!

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值