一起来写OKHttp的拦截器
2017-06-26 09:24
447 查看
00:00
一开始就不多说废话了,主要因为工作时遇到了一些使用 OKHttp 拦截器的问题,所以在此特写这篇以作记录。现如今,做 Android 开发在选择网络框架时,大多数都会首推 Retrofit 。Retrofit 以其简洁优雅的代码俘获了大多数开发者的心。
然而 Retrofit 内部请求也是基于 OKHttp 的,所以在做一些自定义修改 HTTP 请求时,需要对 OKHttp 拦截器具有一定了解。相信熟悉 OKHttp 的同学都知道,OKHttp 内部是使用拦截器来完成请求和响应的,利用的是责任链设计模式。所以可以说,拦截器是 OKHttp 的精髓所在。
那么接下来,我们就通过一些例子来学习怎样编写 OKHttp 的拦截器吧,其实这些例子也正是之前我遇到的情景。
00:01
添加请求
Header
假设现在后台要求我们在请求 API 接口时,都在每一个接口的请求头上添加对应的 token 。使用 Retrofit 比较多的同学肯定会条件反射出以下代码:1 2 3 | @FormUrlEncoded @POST("/mobile/login.htm") Call<ResponseBody> login(@Header("token") String token, @Field("mobile") String phoneNumber, @Field("smsCode") String smsCode); |
相信有良知的程序员都会拒绝,因为这会导致代码的冗余。
那么有没有好的办法可以一劳永逸呢?答案是肯定的,那就要用到拦截器了。
代码很简单:
1 2 34 | public class TokenHeaderInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { // get token String token = AppService.getToken(); Request originalRequest = chain.request(); // get new request, add request header Request updateRequest = originalRequest.newBuilder() .header("token", token) .build(); return chain.proceed(updateRequest); } } |
最后,在 OKHttpClient 中使用:
1 2 34 | OkHttpClient client = new OkHttpClient.Builder() .addNetworkInterceptor(new TokenHeaderInterceptor()) .build(); Retrofit retrofit = new Retrofit.Builder().baseUrl(BuildConfig.BASE_URL) .client(client).addConverterFactory(GsonConverterFactory.create()).build(); |
改变请求体
除了增加请求头之外,拦截器还可以改变请求体。假设现在我们有如下需求:在上面的 login 接口基础上,后台要求我们传过去的请求参数是要按照一定规则经过加密的。
规则如下:
请求参数名统一为content;
content值:JSON 格式的字符串经过 AES 加密后的内容;
举个例子,根据上面的 login 接口,现有
1 | {"mobile":"157xxxxxxxx", "smsCode":"xxxxxx"} |
看完了上面的
TokenHeaderInterceptor之后,这需求对于我们来说可以算是信手拈来:
1 2 34 | public class RequestEncryptInterceptor implements Interceptor { private static final String FORM_NAME = "content"; private static final String CHARSET = "UTF-8"; @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); RequestBody body = request.body(); if (body instanceof FormBody) { FormBody formBody = (FormBody) body; Map<String, String> formMap = new HashMap<>(); // 从 formBody 中拿到请求参数,放入 formMap 中 for (int i = 0; i < formBody.size(); i++) { formMap.put(formBody.name(i), formBody.value(i)); } // 将 formMap 转化为 json 然后 AES 加密 Gson gson = new Gson(); String jsonParams = gson.toJson(formMap); String encryptParams = AESCryptUtils.encrypt(jsonParams.getBytes(CHARSET), AppConstant.getAESKey()); // 重新修改 body 的内容 body = new FormBody.Builder().add(FORM_NAME, encryptParams).build(); } if (body != null) { request = request.newBuilder() .post(body) .build(); } return chain.proceed(request); } } |
经过了这两种拦截器,相信同学们已经充分体会到了 OKHttp 的优点和与众不同。
最后,自定义拦截器的使用情景通常是对所有网络请求作统一处理。如果下次你也碰到这种类似的需求,别忘记使用自定义拦截器哦!
00:02
呃呃呃,按道理来讲应该要结束了。但是,我在这里开启一个番外篇吧,不过目标不是针对拦截器而是 ConverterFactory 。
还是后台需求,login 接口返回的数据也是经过 AES 加密的。所以需要我们针对所有响应体都做解密处理。
另外,还有很重要的一点,就是数据正常和异常时返回的 JSON 格式不一致。
在业务数据正常的时候(即 code 等于 200 时):
1 2 34 | { "code":200, "msg":"请求成功", "data":{ "nickName":"Hello", "userId": "1234567890" } } |
1 2 34 | { "code":7008, "msg":"用户名或密码错误", "data":"用户名或密码错误" } |
那么,如何解决上述的两个问题呢?
利用 自定义 ConverterFactory !!
我们先创建包名
retrofit2.converter.gson,为什么要创建这个包名呢?
因为自定义的 ConverterFactory 需要继承 Converter.Factory ,而 Converter.Factory 类默认是包修饰符。
代码如下:
1 2 34 | public final class CustomConverterFactory extends Converter.Factory { private final Gson gson; public static CustomConverterFactory create() { return create(new Gson()); } @SuppressWarnings("ConstantConditions") // Guarding public API nullability. public static CustomConverterFactory create(Gson gson) { if (gson == null) throw new NullPointerException("gson == null"); return new CustomConverterFactory(gson); } private CustomConverterFactory(Gson gson) { this.gson = gson; } @Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type)); // attention here! return new CustomResponseConverter<>(gson, adapter); } @Override public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type)); return new GsonRequestBodyConverter<>(gson, adapter); } } |
CustomConverterFactory内部是根据
CustomResponseConverter来转化
JSON 的,这才是我们的重点。
1 2 34 | class CustomResponseConverter<T> implements Converter<ResponseBody, T> { private final Gson gson; private final TypeAdapter<T> adapter; private static final String CODE = "code"; private static final String DATA = "data"; CustomResponseConverter(Gson gson, TypeAdapter<T> adapter) { this.gson = gson; this.adapter = adapter; } @Override public T convert(ResponseBody value) throws IOException { try { String originalBody = value.string(); // 先 AES 解密 String body = AESCryptUtils.decrypt(originalBody, AppConstant.getAESKey()); // 再获取 code JSONObject json = new JSONObject(body); int code = json.optInt(CODE); // 当 code 不为 200 时,设置 data 为 null,这样转化就不会出错了 if (code != 200) { Map<String, String> map = gson.fromJson(body, new TypeToken<Map<String, String>>() { }.getType()); map.put(DATA, null); body = gson.toJson(map); } return adapter.fromJson(body); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } finally { value.close(); } } } |
最后就是使用了
CustomConverterFactory:
1 2 34 | OkHttpClient client = new OkHttpClient.Builder() .addNetworkInterceptor(new TokenHeaderInterceptor()) .addInterceptor(new RequestEncryptInterceptor()) .build(); Retrofit retrofit = new Retrofit.Builder().baseUrl(BuildConfig.BASE_URL) .client(client).addConverterFactory(CustomConverterFactory.create()).build(); |
完结了。
相关文章推荐
- OkHttp3.0源码解析---拦截器
- OkHttp3源码解析02-拦截器
- 网络拦截器 okhttp工具类以及imageloder
- okhttp日志拦截器
- Android——自定义拦截器实现OKHttp网络请求
- OKHttp3的工具类(拦截器,下载,上传)
- OkHttp3源码(九) ------ 拦截器
- OkHttp完全解析(六)拦截器
- Okhttp源码解析之Interceptor(拦截器)
- OKHTTP的拦截器以及https访问
- Okhttp的拦截器
- OKhttp3二次完美封装,内含日志,缓存拦截器
- Android_封装Okhttp+Retrofit+RxJava,外加拦截器
- okhttp 日志拦截器Logging-interceptor
- 浅析 OkHttp 的拦截器机制
- OkHttp的基本使用-3(添加拦截器和GET请求的参数拼接)
- Okhttp框架搭建及拦截器
- OkHttp拦截器
- Android——自定义拦截器实现OKHttp网络请求
- 自定义OKhttp拦截器 添加请求头