在實際項目中,對於有需要統一進行公共參數添加的網絡請求,可以使用下面的代碼來實現:
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(ctx).setRequestInterceptor(new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {
request.addQueryParam("publicParams", "1");
}
}).setConverter(new BaseConverter())
.build();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
在RestAdapter的實例化對象的時候,爲其指定一個RequestInterceptor接口的實現類即可,在該類中,可以對請求體的相關參數進行設置,如addHeader、addQueryParam等。
不過遺憾的是Retrofit2.0已經沒有了該類,該怎麼做呢?通過Interceptor實現。
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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
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();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
有時候你找到了一條線,就能順着線找到更多。
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)
- 1
- 1
post 請求,且 body type 爲 x-www-form-urlencoded 時,鍵值對公共參數插入到 body 參數中,其他情況插入到 url query 參數中。
addParamsMap(Map paramsMap)
- 1
- 1
同上,不過這裏用鍵值對 Map 作爲參數批量插入。
addHeaderParam(String key, String value)
- 1
- 1
在 header 中插入鍵值對參數。
addHeaderParamsMap(Map headerParamsMap)
- 1
- 1
在 header 中插入鍵值對 Map 集合,批量插入。
addHeaderLine(String headerLine)
- 1
- 1
在 header 中插入 headerLine 字符串,字符串需要符合 -1 != headerLine.indexOf(“:”) 的規則,即可以解析成鍵值對。
addHeaderLinesList(List headerLinesList)
- 1
- 1
同上,headerLineList: List 爲參數,批量插入 headerLine。
addQueryParam(String key, String value)
- 1
- 1
插入鍵值對參數到 url query 中。
addQueryParamsMap(Map queryParamsMap)
- 1
- 1
插入鍵值對參數 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();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
TODO
- 自動時間戳公共參數的支持
- 動態參數的支持(例如登錄後插入服務器返回的 uid)
源碼
源碼與引用:https://github.com/jkyeo/okhttp-basicparamsinterceptor
basicparamsinterceptor應用
配置基本提交參數
我們可以建一個攔截器,這裏我舉例加些簡單的系統參數,如下:
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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
上面Utils類是使用的okio.Buffer裏面的工具類。通過RequestBody構建要上傳的一些基本公共的參數,然後通過”&”符號在http 的body裏面其他要提交參數拼接。然後再通過requestBuilder重新創建request對象,然後再通過chain.proceed(request)返回Response 。
接下來在創建OkHttpClient對象的時候修改爲如下代碼:
mOkHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.addInterceptor(new HttpBaseParamsLoggingInterceptor())
.build();
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
這樣就添加好了一些基本的公共參數。
下面我們藉助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;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
只要像上面一樣配置就行了。