记录日常用经常使用的拦截器的骚操作
- 重试拦截器
- Token 失效自动刷新,并重新请求
- URL 重定向
- 请求体数据加密
1. 重试拦截器
OkHttp 自带 retryOnConnectionFailure(true) 方法可以实现重试,但是不支持自定义重试次数.
所以在项目中需要自定义重试拦截器.
public class RetryIntercepter implements Interceptor {
public int maxRetry; //最大重试次数
private int retryNum = 0; //假如设置为3次重试的话,则最大可能请求4次(默认1次+3次重试)
public RetryIntercepter(int maxRetry) {
this.maxRetry = maxRetry;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
while (!response.isSuccessful() && retryNum < maxRetry) {
retryNum++;
response = chain.proceed(request);
}
return response;
}
}
在设置重试拦截器时, 需要关闭默认的重试方法 retryOnConnectionFailure(false)
2. Token 自动刷新拦截器
- Token 在 header 中
public class TokenInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response originalResponse = chain.proceed(request);
if (isTokenExpired(originalResponse)) { //token 失效 1、这里根据自己的业务需求写判断条件
return handleTokenInvalid(chain, request);
}
return originalResponse;
}
private boolean isTokenExpired(Response response) {
if (response.code() == 401) {
return true;
}
return false;
}
// 3. 处理token失效问题
private Response handleTokenInvalid(Chain chain, Request request) throws IOException {
String token = refreshToken();
Request newRequest;
if (!TextUtils.isEmpty(token)) { // 刷新成功,重新签名
newRequest = request.newBuilder().removeHeader("token").addHeader("token", token).build();
} else {
newRequest = request;
}
return chain.proceed(newRequest);
}
//刷新token
private String refreshToken() {
String token = "";// 2. 获取新 token
return token;
}
}
- Token 在 Cookie 中
public class TokenInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if (isTokenExpired(response)) {
if (refreshToken()) {
return chain.proceed(request);
} else {
// 刷新 token 失效,回到登录页,或者其它业务.
}
}
return response;
}
private boolean isTokenExpired(Response response) {
if (response.code() == 401) {
return true;
}
return false;
}
// 1. 获取新 token
// 必须使用同步请求
private boolean refreshToken() {
// 发送同步请求获取新 token
return true;
}
}
// 使用
OkHttpClient client = new OkHttpClient.Builder()
.cookieJar(new CookieJar()) // 根据业务实现的 cookiejar
.addInterceptor(new TokenInterceptor())
.build();
如果 token 是在 cookie 中的,在设置创建 OkHttpClient 时一定要先设置 cookieJar 在设置 Token 拦截器
这样就无需手动设置 token 即可每次在 cookieJar 中获取想的 token
3. URL 重定向
在 okhttp 中,如果请求重定向是 http->https 或者 https->http 是,有事携带的参数会丢失.需要自己定义重定向拦截器.
//处理重定向的拦截器
public class RedirectInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
okhttp3.Request request = chain.request();
Response response = chain.proceed(request);
int code = response.code();
if (isFollowRedirects(code)) {
//获取重定向的地址
String location = response.headers().get("Location");
//重新构建请求
Request newRequest = request.newBuilder().url(location).build();
response = chain.proceed(newRequest);
}
return response;
}
// 根据业务自定义重定向条件
private boolean isFollowRedirects(int code) {
switch (code) {
case 300:
case 301:
return true;
default:
return false;
}
}
}
使用
OkHttpClient client = new OkHttpClient.Builder()
.followRedirects(false); //禁制OkHttp的重定向操作,我们自己处理重定向
.addInterceptor(new RedirectInterceptor())
.build();
4.加解密拦截器
以 POST 表单请求为例
public class EncryptOrDecryptInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// 当为 post 表单请求时才进行加密
if (request.method().equals("POST")) {
RequestBody body = request.body();
if (body instanceof FormBody) {
FormBody.Builder builder = new FormBody.Builder();
FormBody formBody = (FormBody) body;
// 为每个参数进行加密
for (int i = 0; i < formBody.size(); i++) {
builder.add(formBody.name(i), AESUtil.encrypt(formBody.value(i)));
request = request.newBuilder()
.post(builder.build())
.build();
}
}
}
// 发送请求
Response response = chain.proceed(request);
if (response.isSuccessful()) {
ResponseBody responseBody = response.body();
if (responseBody != null) {
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE);
Buffer buffer = source.buffer();
Charset charset = Charset.forName("UTF-8");
MediaType contentType = responseBody.contentType();
if (contentType != null) {
charset = contentType.charset(charset);
}
// 1. 获取 response body
String bodyString = buffer.clone().readString(charset);
// 2. 将 body 进行解密,需根据项目进行定制
String decryptBody = AESUtil.decrypt(bodyString);
// 3. 构建新的 response,并将解密后的 response body 返回
ResponseBody newResponseBody = ResponseBody.create(contentType, decryptBody);
response = response.newBuilder().body(newResponseBody).build();
}
}
return response;
}
}
网友评论