1. 程式人生 > >Retrofit2.0 公共引數(固定引數)

Retrofit2.0 公共引數(固定引數)

在實際專案中,對於有需要統一進行公共引數新增的網路請求,可以使用下面的程式碼來實現:

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等。

不過遺憾的是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); } }

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();

有時候你找到了一條線,就能順著線找到更多。

背景

在 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)
    原始碼

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);
        }
    }

上面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;
        }

    }
}

只要像上面一樣配置就行了。

相關推薦

Retrofit2.0 公共引數固定引數

在實際專案中,對於有需要統一進行公共引數新增的網路請求,可以使用下面的程式碼來實現: RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint(ct

Android Hack Retrofit 增強引數固定引數

誰是 Retrofit 此處省去幾百字。。。。 可以看原文介紹。 需求是折騰的動力源泉 話說我最近開始忙活一個跟服務端互動頗多的專案,其中涉及到的全部 HTTP 請求都需要傳入 5 個相同的引數,並需要根據其他所有引數動態生成一個引數。當時也

初始化引數Initialization Parameter知識合集 based on 11g

初始化引數檔案分為: 1)pfile 靜態引數檔案 2)spfile 動態伺服器引數檔案 作用:儲存建立例項、啟動後臺程序所需引數值。 呼叫:例項啟動時,按如下順序調取初始化引數檔案 linux: $ORACLE_HOME/dbs/spfile<SID>.ora $ORACLE

Java中不定項引數可變引數的作用和使用方式

引言:   我們在編寫方法的過程中,可能會遇見一個方法有不確定引數個數的情況。一般我們會用方法過載來解決問題: //方法過載,解決引數個數不確定問題 public void method(); public void method(int i); public void method(int

函式引數預設引數、可變引數、關鍵字引數

原文地址:https://www.cnblogs.com/mingshengling/p/7842826.html 1、預設引數 預設引數降低了函式呼叫的難度,而一旦需要更復雜的呼叫時,又可以傳遞更多的引數來實現。無論是簡單呼叫還是複雜呼叫,函式只需要定義一個。 有多個預設引數時,呼叫的時候,

大地測量——計算七引數程式設計作業

張家誠 2016301610045 需求:通過A,B座標系中提供的六個已知同名點,獲得由A轉換到B座標系的七個引數。 邏輯框圖:                       &nbs

網站分析引數例項分析SimilarWeb外掛引數

閒來無事,看小米官網的時候,點開了之前安裝的similar web外掛,對於網站分析也挺感興趣,藉著這個外掛工具,就初步瞭解一下網站分析相關。 那麼這些指標是什麼意思呢? SimilarWeb Rank:類似網站排名 Global Rank:全球網站排名 第三欄一般是類別,大概網站從事範圍,比如進

預設引數備胎

預設預設引數的定義:   宣告或定義函式時為函式的引數指定一個預設值,在呼叫該函式時,   ①如果沒有指定實參則採用該預設值,   ②否則使用指定的實參 #include<iostream> using namspace std; void Tes

不定引數rest 引數 ...

不定引數 如何實現不定引數 使用過 underscore.js 的人,肯定都使用過以下幾個方法: _.without(array, *values) //返回一個刪除所有values值後的array副本 _.union(*arrays) //返回傳入的arrays(陣列)並集 _.difference

SAP_ABAP_GS01/GS02/GS03資料集_引數條件表靈活配置GS01/GS02/GS03

在開發中,某段程式碼執行可能需要滿足某個條件,通常解決辦法有兩種:一種是在程式碼中寫死限制條件,此種方式當限制條件變化時需要修改程式碼;另一種辦法則是自定義資料表,將限制條件值儲存在表中,當程式執行時,可以直接從表中讀取條件值作為控制條件,這樣比較靈活,就像Java開發中的屬

SpringMVC第八篇——使用陣列接收引數批量刪除

選中批量進行刪除: 頁面提交的form表單 <form action="abc" method="post"> 1<input type="checkbox" na

EXCEL-VBA:Workbooks.Open 引數 開啟檔案

開啟一個工作簿。 語法 表示式 . Open( FileName , UpdateLinks , ReadOnly , Format , Password , WriteResPassword , IgnoreReadOnlyRecommended , Origin , 

如何用 linux 實現命令列引數可變引數實現

僅用main函式的引數實現一個整數計算器 #include <stdio.h> #include <string.h> #include <stdlib.h>

如何向OrderBy傳遞字串引數Entity Framework

AppBox 是基於 FineUI 的通用許可權管理框架,包括使用者管理、職稱管理、部門管理、角色管理、角色許可權管理等模組。 Entity Framework提供的排序功能  再來回顧一下上篇文章,載入使用者列表並進行排序資料庫分頁的程式碼: var q = DB.Users.Incl

正則表示式有無g引數全域性搜尋時,test()結果的差異

有g引數的正則表示式: var kk=//w/w/g;var kk=new RegExp("//w//w","g"); 如果正則表示式有指定g引數全域性匹配 , 則每次test()是依次獲得下一個匹配。舉例說明: var s="AABBCC";var kk=/(/w/w)

spring接收json格式的多個物件引數變通法

兩種方法 方法1 如果使用spring mvc同客戶端通訊,完全使用json資料格式,需要如下定義一個RequestMapping @Controller public class TestController{ @RequestMapping("\test"

js通過路徑傳引數頁面間

A頁面return '<span class="name" style="text-align: center;">' +'<a href="/manage/customer_details?acId='+obj.aData.acId+'">' +'檢

delphi如何用儲存資料庫連線引數INI篇

在設計資料庫應用程式的時候,經常需要將一些資訊從程式中獨立出來,以保證程式的可移植性。其中最重要的資訊就是資料庫的連線引數。在Delphi中,獲得正確的資料庫連線引數的方法十分簡單,你只需要建立一個數據模組,在其中新增一個ADO Connection,雙擊之,然後在彈出的視窗

get請求,中文亂碼解決 之 百度如何正確獲取中文請求引數我猜測

url查詢串不直接支援中文等非ASCII碼錶符號的傳輸,需要經過瀏覽器採取一種編碼方案將中文字元編碼為可用於url傳輸的符號(也就是ASCII碼的符號)。除這種類似中文查詢串的情況外,url中有特殊用途的符號,例如%,@, :, &,#,?等也不能直接傳輸,同樣需

使用 getopt 處理命令列長引數長選項

getopt命令並不是bash的內建命令,它是由util-linux包提供的外部命令。 getopt 與 getopts 的區別 getopts 是 shell 內建命令, getopt 是一個獨