為Retrofit統(tǒng)一添加post請(qǐng)求的默認(rèn)參數(shù)的方法
最近在學(xué)習(xí)使用Retrofit,并嘗試將之引入到現(xiàn)有的項(xiàng)目中來(lái)。大家都知道,在Http請(qǐng)求中我們使用 Content-Type 來(lái)指定不同格式的請(qǐng)求信息:
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í)際項(xiàng)目中通常最后的請(qǐng)求參數(shù)都包含默認(rèn)的一些參數(shù)(Token,Api版本、App版本等)和普通的請(qǐng)求參數(shù)。網(wǎng)上有很多關(guān)于第一種 Content-Type 添加默認(rèn)參數(shù)的方法。而在我現(xiàn)有項(xiàng)目上,除文件上傳外絕大多數(shù)請(qǐng)求都走了 post + application/json 的方式。這里暫不討論兩者的優(yōu)缺點(diǎn),而是談下 Content-Type 為 application/json 時(shí),如何添加默認(rèn)參數(shù)。
傳統(tǒng)方式:
我們先來(lái)回憶下兩種方式
public interface Apis { @POST("user/login") Observable<Entity<User>> login(@Body RequestBody body);//構(gòu)造一個(gè)RequestBody對(duì)象 @POST("user/login") Observable<Entity<User>> login(@Body LoginInfo loginInfo);//構(gòu)造一個(gè)實(shí)體對(duì)象 }
第二種方法,你需要為每一個(gè)請(qǐng)求的對(duì)象創(chuàng)建一個(gè)不同的Model,太麻煩了,這里選擇第一種直接構(gòu)造RequestBody對(duì)象:
Retrofit mRetrofit = new Retrofit.Builder() .baseUrl(HttpConfig.BASE_URL) .addConverterFactory(GsonConverterFactory.create())//添加gson轉(zhuǎn)換器 .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//添加rxjava轉(zhuǎn)換器 .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í)行后通過(guò)抓包查看,請(qǐng)求體如下:
而我希望的結(jié)果是這樣的:
當(dāng)然我們可以每次構(gòu)造 RequestBody,在傳入的參數(shù)中加入默認(rèn)參數(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)); }
這樣完全沒(méi)問(wèn)題,但不夠優(yōu)雅,所以接下來(lái)我們來(lái)討論我所想到的一種方式
攔截器方式:
哈哈,相信熟悉OkHttp的同學(xué)已經(jīng)想到這種方式了,是的很多網(wǎng)上關(guān)于第一種 Content-Type 添加默認(rèn)參數(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(); //把原來(lái)的參數(shù)添加到新的構(gòu)造器,(因?yàn)闆](méi)找到直接添加,所以就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對(duì)象,然后拿到了requestBody對(duì)象,然后 判斷是不是FormBody類(lèi)型,如果是的話,將里面的鍵值對(duì)取出,并添加默認(rèn)參數(shù)的鍵值對(duì)并構(gòu)造出一個(gè)新的formBody對(duì)象,最后將原來(lái)用request對(duì)象構(gòu)造出新的一個(gè)request對(duì)象,將新的formBody對(duì)象穿進(jìn)去,攔截器返回。formBody對(duì)象是 Content-Type 為 application/x-www-form-urlencoded 時(shí),Retrofit為我們生成的對(duì)象,它是RequestBody的子類(lèi);而 Content-Type 為 application/json 時(shí),生成的就是 RequestBody (準(zhǔn)確的說(shuō)是匿名子類(lèi))。所以我們只要繼承重寫(xiě) RequestBody ,記錄請(qǐng)求內(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ù),并添加新的默認(rèn)參數(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()); }
這樣在外面我們只要改動(dòng)一行代碼就可以實(shí)現(xiàn)全局添加默認(rèn)參數(shù):
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=utf-8"),JsonHelper.toJSONString(params));
替換為:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 簡(jiǎn)略分析Android的Retrofit應(yīng)用開(kāi)發(fā)框架源碼
- Android app開(kāi)發(fā)中Retrofit框架的初步上手使用
- android Retrofit2+okHttp3使用總結(jié)
- 詳解RxJava2 Retrofit2 網(wǎng)絡(luò)框架簡(jiǎn)潔輕便封裝
- Android Retrofit 中文亂碼問(wèn)題的解決辦法
- Android網(wǎng)絡(luò)請(qǐng)求框架Retrofit詳解
- Android如何通過(guò)Retrofit提交Json格式數(shù)據(jù)
- Retrofit實(shí)現(xiàn)圖文上傳至服務(wù)器
- 詳解Retrofit2.0 公共參數(shù)(固定參數(shù))
- 如何動(dòng)態(tài)改變Retrofit的base url和rest版本詳解
相關(guān)文章
Android的廣播Receiver動(dòng)態(tài)注冊(cè)和靜態(tài)注冊(cè)示例
本篇文章主要介紹了Android的廣播Receiver動(dòng)態(tài)注冊(cè)和靜態(tài)注冊(cè)示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02Android中3種全屏方法及3種去掉標(biāo)題欄的方法
這篇文章主要介紹了Android中3種全屏方法及3種去掉標(biāo)題欄的方法,二個(gè)問(wèn)題各給出了3種解決方法,并給出實(shí)例代碼,需要的朋友可以參考下2015-06-06Android基于高德地圖poi的仿微信獲取位置功能實(shí)例代碼
這篇文章主要介紹了Android基于高德地圖poi的仿微信獲取位置功能,當(dāng)用戶打開(kāi)頁(yè)面自動(dòng)定位,同時(shí)搜索周邊所有poi,點(diǎn)擊搜索按鈕輸入關(guān)鍵字,獲取關(guān)鍵字搜索結(jié)果,本文圖文并茂給大家介紹的非常詳細(xì),需要的朋友參考下吧2017-12-12Android?WebRTC?對(duì)?AudioRecord?的使用技術(shù)分享
這篇文章主要介紹了Android?WebRTC?對(duì)?AudioRecord?的使用技術(shù)分享,AudioRecord?是?Android?基于原始PCM音頻數(shù)據(jù)錄制的類(lèi),接下來(lái)和小編進(jìn)入文章了解更詳細(xì)的內(nèi)容吧2022-02-02Android實(shí)現(xiàn)仿魅族日歷首頁(yè)功能
這篇文章主要介紹了Android實(shí)現(xiàn)仿魅族日歷首頁(yè)功能的實(shí)現(xiàn)過(guò)程以及相關(guān)代碼講解分享,對(duì)此有興趣的朋友參考下。2018-02-02Android中使用 AutoCompleteTextView 實(shí)現(xiàn)手機(jī)號(hào)格式化附帶清空歷史的操作
有個(gè)小伙伴遇到了這樣一個(gè)問(wèn)題,就是AutoCompleteTextView實(shí)現(xiàn)自動(dòng)填充的功能。同時(shí)要具備手機(jī)格式化的功能。接下來(lái)通過(guò)本文給大家分享使用 AutoCompleteTextView 實(shí)現(xiàn)手機(jī)號(hào)格式化附帶清空歷史的操作方法,需要的朋友參考下2017-03-03Android獲取點(diǎn)擊屏幕的位置坐標(biāo)
這篇文章主要為大家詳細(xì)介紹了Android獲取點(diǎn)擊屏幕的位置坐標(biāo),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05android中SharedPreferences實(shí)現(xiàn)存儲(chǔ)用戶名功能
本篇文章主要介紹了android中SharedPreferences實(shí)現(xiàn)保存用戶名功能,詳細(xì)的介紹了SharedPreferences的功能,需要的朋友可以參考下2017-04-04Android使用ListView實(shí)現(xiàn)下拉刷新及上拉顯示更多的方法
這篇文章主要介紹了Android使用ListView實(shí)現(xiàn)下拉刷新及上拉顯示更多的方法,結(jié)合實(shí)例形式分析了ListView滾動(dòng)刷新與加載的相關(guān)操作技巧,需要的朋友可以參考下2017-02-02