请先阅读:
Retrofit 动态参数(非固定参数、非必须参数)(Get、Post请求)在实际项目中,对于有需要统一进行公共参数添加的网络请求,可以使用下面的代码来实现:
RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint(ctx).setRequestInterceptor(new RequestInterceptor() { @Override public void intercept(RequestFacade request) { request.addQueryParam("publicParams", "1"); } }).setConverter(new BaseConverter()) .build();
在RestAdapter的实例化对象的时候,为其指定一个RequestInterceptor接口的实现类即可,在该类中,可以对请求体的相关参数进行设置,如addHeader、addQueryParam等。
Interceptor是拦截器, 在发送之前, 添加一些参数, 或者获取一些信息。
/** * 封装公共参数(Key和密码) * <p> */ public class CommonInterceptor implements Interceptor { private final String mApiKey; private final String mApiSecret; public CommonInterceptor(String apiKey, String apiSecret) { mApiKey = apiKey; mApiSecret = apiSecret; } @Override public Response intercept(Interceptor.Chain chain) throws IOException { String marvelHash = ApiUtils.generateMarvelHash(mApiKey, mApiSecret); Request oldRequest = chain.request(); // 添加新的参数 HttpUrl.Builder authorizedUrlBuilder = oldRequest.url() .newBuilder() .scheme(oldRequest.url().scheme()) .host(oldRequest.url().host()) .addQueryParameter(MarvelService.PARAM_API_KEY, mApiKey) .addQueryParameter(MarvelService.PARAM_TIMESTAMP, ApiUtils.getUnixTimeStamp()) .addQueryParameter(MarvelService.PARAM_HASH, marvelHash); // 新的请求 Request newRequest = oldRequest.newBuilder() .method(oldRequest.method(), oldRequest.body()) .url(authorizedUrlBuilder.build()) .build(); return chain.proceed(newRequest); } }
Okhttp3使用了装饰者模式, 使用Builder添加Interceptor。
CommonInterceptor commonInterceptor = new CommonInterceptor( "key", "Secret"); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(commonInterceptor) .build(); // 适配器 Retrofit retrofit = new Retrofit.Builder() .baseUrl("url") .addConverterFactory(GsonConverterFactory.create() .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .client(client) .build();
有时候你找到了一条线,就能顺着线找到更多。
BasicParamsInterceptor - 为 OkHttp 请求添加公共参数
在 Android Http API 请求开发中经常遇到这样的需求:每一次请求带上一个或者多个固定不变的参数,例如:
设备唯一标识:device_id = 7a4391e28f309c21
业务唯一标识:uid = 2231001
平台类型:platform = android
客户端版本号:version_code = 6
…
这些参数是每一次发生请求都需要的,我们姑且称他们为公共参数(或者基础参数)。公共参数一般以 header line、url query 或者 post body(较少) 这些形式插入请求。
如果使用 OkHttp 作为 http request client, 这件事情就变得简单多了。OkHttp 提供了强大的拦截器组件 (Interceptor):
Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls.
也就是说,OkHttp 的拦截器功能之一就是对将要发出的请求进行拦截、改造然后再发出。这正是我们想要的。BasicParamsInterceptor 实现了 okhttp3.Interceptor 接口。
实现 public Response intercept(Chain chain) throws IOException 方法。使用 Builder 模式,暴露以下接口:
addParam(String key, String value)
post 请求,且 body type 为 x-www-form-urlencoded 时,键值对公共参数插入到 body 参数中,其他情况插入到 url query 参数中。
addParamsMap(Map paramsMap)
同上,不过这里用键值对 Map 作为参数批量插入。
addHeaderParam(String key, String value)
在 header 中插入键值对参数。
addHeaderParamsMap(Map headerParamsMap)
在 header 中插入键值对 Map 集合,批量插入。
addHeaderLine(String headerLine)
在 header 中插入 headerLine 字符串,字符串需要符合 -1 != headerLine.indexOf(“:”) 的规则,即可以解析成键值对。
addHeaderLinesList(List headerLinesList)
同上,headerLineList: List 为参数,批量插入 headerLine。
addQueryParam(String key, String value)
插入键值对参数到 url query 中。
addQueryParamsMap(Map queryParamsMap)
插入键值对参数 map 到 url query 中,批量插入。
示例
使用 Buider 模式创建 Interceptor 对象,然后调用 OkHttp 的 addInterceptor(Interceptor i) 方法将 interceptor 对象添加至 client 中:
BasicParamsInterceptor basicParamsInterceptor = new OkPublicParamsInterceptor.Builder() .addHeaderParam("device_id", DeviceUtils.getDeviceId()) .addParam("uid", UserModel.getInstance().getUid()) .addQueryParam("api_version", "1.1") .build(); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(basicParamsInterceptor) .build();
TODO
自动时间戳公共参数的支持
动态参数的支持(例如登录后插入服务器返回的 uid)
源码
源码与引用: https://github.com/jkyeo/okht...
配置基本提交参数
我们可以建一个拦截器,这里我举例加些简单的系统参数,如下:
class HttpBaseParamsLoggingInterceptor implements Interceptor{ @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Request.Builder requestBuilder = request.newBuilder(); RequestBody formBody = new FormBody.Builder() .add("userId", "10000") .add("sessionToken", "E34343RDFDRGRT43RFERGFRE") .add("q_version", "1.1") .add("device_id", "android-344365") .add("device_os", "android") .add("device_osversion","6.0") .add("req_timestamp", System.currentTimeMillis() + "") .add("app_name","forums") .add("sign", "md5") .build(); String postBodyString = Utils.bodyToString(request.body()); postBodyString += ((postBodyString.length() > 0) ? "&" : "") + Utils.bodyToString(formBody); request = requestBuilder .post(RequestBody.create(MediaType.parse("application/x-www-form-urlencoded;charset=UTF-8"), postBodyString)) .build(); return chain.proceed(request); } }
上面Utils类是使用的okio.Buffer里面的工具类。通过RequestBody构建要上传的一些基本公共的参数,然后通过”&”符号在http 的body里面其他要提交参数拼接。然后再通过requestBuilder重新创建request对象,然后再通过chain.proceed(request)返回Response 。
接下来在创建OkHttpClient对象的时候修改为如下代码:
mOkHttpClient = new OkHttpClient.Builder() .addInterceptor(interceptor) .addInterceptor(new HttpBaseParamsLoggingInterceptor()) .build();
这样就添加好了一些基本的公共参数。
下面我们借助BasicParamsInterceptor实现,代码如下:
public class BasicParamsInterceptor implements Interceptor { Map<String, String> queryParamsMap = new HashMap<>(); Map<String, String> paramsMap = new HashMap<>(); Map<String, String> headerParamsMap = new HashMap<>(); List<String> headerLinesList = new ArrayList<>(); private BasicParamsInterceptor() { } @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Request.Builder requestBuilder = request.newBuilder(); // process header params inject Headers.Builder headerBuilder = request.headers().newBuilder(); if (headerParamsMap.size() > 0) { Iterator iterator = headerParamsMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = (Map.Entry) iterator.next(); headerBuilder.add((String) entry.getKey(), (String) entry.getValue()); } } if (headerLinesList.size() > 0) { for (String line: headerLinesList) { headerBuilder.add(line); } } requestBuilder.headers(headerBuilder.build()); // process header params end // process queryParams inject whatever it's GET or POST if (queryParamsMap.size() > 0) { injectParamsIntoUrl(request, requestBuilder, queryParamsMap); } // process header params end // process post body inject if (request.method().equals("POST") && request.body().contentType().subtype().equals("x-www-form-urlencoded")) { FormBody.Builder formBodyBuilder = new FormBody.Builder(); if (paramsMap.size() > 0) { Iterator iterator = paramsMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = (Map.Entry) iterator.next(); formBodyBuilder.add((String) entry.getKey(), (String) entry.getValue()); } } RequestBody formBody = formBodyBuilder.build(); String postBodyString = bodyToString(request.body()); postBodyString += ((postBodyString.length() > 0) ? "&" : "") + bodyToString(formBody); requestBuilder.post(RequestBody.create(MediaType.parse("application/x-www-form-urlencoded;charset=UTF-8"), postBodyString)); } else { // can't inject into body, then inject into url injectParamsIntoUrl(request, requestBuilder, paramsMap); } request = requestBuilder.build(); return chain.proceed(request); } // func to inject params into url private void injectParamsIntoUrl(Request request, Request.Builder requestBuilder, Map<String, String> paramsMap) { HttpUrl.Builder httpUrlBuilder = request.url().newBuilder(); if (paramsMap.size() > 0) { Iterator iterator = paramsMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = (Map.Entry) iterator.next(); httpUrlBuilder.addQueryParameter((String) entry.getKey(), (String) entry.getValue()); } } requestBuilder.url(httpUrlBuilder.build()); } private static String bodyToString(final RequestBody request){ try { final RequestBody copy = request; final Buffer buffer = new Buffer(); if(copy != null) copy.writeTo(buffer); else return ""; return buffer.readUtf8(); } catch (final IOException e) { return "did not work"; } } public static class Builder { BasicParamsInterceptor interceptor; public Builder() { interceptor = new BasicParamsInterceptor(); } public Builder addParam(String key, String value) { interceptor.paramsMap.put(key, value); return this; } public Builder addParamsMap(Map<String, String> paramsMap) { interceptor.paramsMap.putAll(paramsMap); return this; } public Builder addHeaderParam(String key, String value) { interceptor.headerParamsMap.put(key, value); return this; } public Builder addHeaderParamsMap(Map<String, String> headerParamsMap) { interceptor.headerParamsMap.putAll(headerParamsMap); return this; } public Builder addHeaderLine(String headerLine) { int index = headerLine.indexOf(":"); if (index == -1) { throw new IllegalArgumentException("Unexpected header: " + headerLine); } interceptor.headerLinesList.add(headerLine); return this; } public Builder addHeaderLinesList(List<String> headerLinesList) { for (String headerLine: headerLinesList) { int index = headerLine.indexOf(":"); if (index == -1) { throw new IllegalArgumentException("Unexpected header: " + headerLine); } interceptor.headerLinesList.add(headerLine); } return this; } public Builder addQueryParam(String key, String value) { interceptor.queryParamsMap.put(key, value); return this; } public Builder addQueryParamsMap(Map<String, String> queryParamsMap) { interceptor.queryParamsMap.putAll(queryParamsMap); return this; } public BasicParamsInterceptor build() { return interceptor; } } }
只要像上面一样配置就行了。