為Retrofit統(tǒng)一添加post請求的默認參數(shù)的方法
最近在學習使用Retrofit,并嘗試將之引入到現(xiàn)有的項目中來。大家都知道,在Http請求中我們使用 Content-Type 來指定不同格式的請求信息:
APP_FORM_URLENCODED("application/x-www-form-urlencoded"),
APP_JSON("application/json"),
APP_OCTET_STREAM("application/octet-stream"),
MULTIPART_FORM_DATA("multipart/form-data"),
TEXT_HTML("text/html"),
TEXT_PLAIN("text/plain"),
實際項目中通常最后的請求參數(shù)都包含默認的一些參數(shù)(Token,Api版本、App版本等)和普通的請求參數(shù)。網(wǎng)上有很多關于第一種 Content-Type 添加默認參數(shù)的方法。而在我現(xiàn)有項目上,除文件上傳外絕大多數(shù)請求都走了 post + application/json 的方式。這里暫不討論兩者的優(yōu)缺點,而是談下 Content-Type 為 application/json 時,如何添加默認參數(shù)。
傳統(tǒng)方式:
我們先來回憶下兩種方式
public interface Apis {
@POST("user/login")
Observable<Entity<User>> login(@Body RequestBody body);//構造一個RequestBody對象
@POST("user/login")
Observable<Entity<User>> login(@Body LoginInfo loginInfo);//構造一個實體對象
}
第二種方法,你需要為每一個請求的對象創(chuàng)建一個不同的Model,太麻煩了,這里選擇第一種直接構造RequestBody對象:
Retrofit mRetrofit = new Retrofit.Builder()
.baseUrl(HttpConfig.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())//添加gson轉換器
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//添加rxjava轉換器
.client(new OkHttpClient.Builder().build())
.build();
Apis mAPIFunction = mRetrofit.create(Apis.class);
Map<String, Object> params = new LinkedHashMap<>();
params.put("name", "吳彥祖");
params.put("request", "123456");
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JsonHelper.toJSONString(params));
mAPIFunction.login(RequestBody.create(requestBody))
執(zhí)行后通過抓包查看,請求體如下:

而我希望的結果是這樣的:

當然我們可以每次構造 RequestBody,在傳入的參數(shù)中加入默認參數(shù):
public static RequestBody getRequestBody(HashMap<String, Object> hashMap) {
Map<String, Object> params = new LinkedHashMap<>();
params.put("auth", getBaseParams());
params.put("request", hashMap);
return RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JsonHelper.toJSONString(params));
}
這樣完全沒問題,但不夠優(yōu)雅,所以接下來我們來討論我所想到的一種方式
攔截器方式:
哈哈,相信熟悉OkHttp的同學已經(jīng)想到這種方式了,是的很多網(wǎng)上關于第一種 Content-Type 添加默認參數(shù)也是這么做的(原文鏈接):
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (request.method().equals("POST")) {
if (request.body() instanceof FormBody) {
FormBody.Builder bodyBuilder = new FormBody.Builder();
FormBody formBody = (FormBody) request.body();
//把原來的參數(shù)添加到新的構造器,(因為沒找到直接添加,所以就new新的)
for (int i = 0; i < formBody.size(); i++) {
bodyBuilder.addEncoded(formBody.encodedName(i), formBody.encodedValue(i));
}
formBody = bodyBuilder
.addEncoded("clienttype", "1")
.addEncoded("imei", "imei")
.addEncoded("version", "VersionName")
.addEncoded("timestamp", String.valueOf(System.currentTimeMillis()))
.build();
request = request.newBuilder().post(formBody).build();
}
return chain.proceed(request);
}
在上面,我們拿到了request對象,然后拿到了requestBody對象,然后 判斷是不是FormBody類型,如果是的話,將里面的鍵值對取出,并添加默認參數(shù)的鍵值對并構造出一個新的formBody對象,最后將原來用request對象構造出新的一個request對象,將新的formBody對象穿進去,攔截器返回。formBody對象是 Content-Type 為 application/x-www-form-urlencoded 時,Retrofit為我們生成的對象,它是RequestBody的子類;而 Content-Type 為 application/json 時,生成的就是 RequestBody (準確的說是匿名子類)。所以我們只要繼承重寫 RequestBody ,記錄請求內(nèi)容,再將它在攔截器里取出加入并處理就行了。
public class PostJsonBody extends RequestBody {
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
private static final Charset charset = Util.UTF_8;
private String content;
public PostJsonBody(@NonNull String content) {
this.content = content;
}
public String getContent() {
return content;
}
@Nullable
@Override
public MediaType contentType() {
return JSON;
}
@Override
public void writeTo(@NonNull BufferedSink sink) throws IOException {
byte[] bytes = content.getBytes(charset);
if (bytes == null) throw new NullPointerException("content == null");
Util.checkOffsetAndCount(bytes.length, 0, bytes.length);
sink.write(bytes, 0, bytes.length);
}
public static RequestBody create(@NonNull String content) {
return new PostJsonBody(content);
}
}
攔截器里面取出原始json數(shù)據(jù),并添加新的默認參數(shù):
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
Request originalRequest = chain.request();
Request.Builder builder = originalRequest.newBuilder();
if (originalRequest.method().equals("POST")) {
RequestBody requestBody = originalRequest.body();
if (requestBody instanceof PostJsonBody) {
String content = ((PostJsonBody) requestBody).getContent();
HashMap<String, Object> hashMap = JsonHelper.fromJson(content, HashMap.class);
builder.post(RequestBodyFactory.getRequestBody(hashMap));
}
}
return chain.proceed(builder.build());
}
這樣在外面我們只要改動一行代碼就可以實現(xiàn)全局添加默認參數(shù):
RequestBody requestBody =
RequestBody.create(MediaType.parse("application/json;charset=utf-8"),JsonHelper.toJSONString(params));
替換為:
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
- 簡略分析Android的Retrofit應用開發(fā)框架源碼
- Android app開發(fā)中Retrofit框架的初步上手使用
- android Retrofit2+okHttp3使用總結
- 詳解RxJava2 Retrofit2 網(wǎng)絡框架簡潔輕便封裝
- Android Retrofit 中文亂碼問題的解決辦法
- Android網(wǎng)絡請求框架Retrofit詳解
- Android如何通過Retrofit提交Json格式數(shù)據(jù)
- Retrofit實現(xiàn)圖文上傳至服務器
- 詳解Retrofit2.0 公共參數(shù)(固定參數(shù))
- 如何動態(tài)改變Retrofit的base url和rest版本詳解
相關文章
Android中使用socket通信實現(xiàn)消息推送的方法詳解
這篇文章主要介紹了Android中使用socket通信實現(xiàn)消息推送的方法,文中舉了一個消息發(fā)送端和一個消息接收端以及服務器端的例子來說明原理并且展示了客戶端的實現(xiàn),需要的朋友可以參考下2016-04-04
Android Service中使用Toast無法正常顯示問題的解決方法
這篇文章主要介紹了Android Service中使用Toast無法正常顯示問題的解決方法,分析了Service中Toast無法正常顯示的原因與相關的解決方法,具有一定參考借鑒價值,需要的朋友可以參考下2016-10-10
Android中CountDownTimer倒計時器用法實例
這篇文章主要介紹了Android中CountDownTimer倒計時器用法,以實例形式分析了Android中CountDownTimer類的相關使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-10-10

