Retrofit進(jìn)階:自定義注解實現(xiàn)Token參數(shù)注入

通常來說,移動端的登錄身份狀態(tài)是通過token實現(xiàn)的,有關(guān)個人信息的資源和操作的服務(wù)端接口會要求傳入token參數(shù)來實現(xiàn)身份驗證桑李。所以在大部分接口里我們都要寫一個@Query("token")token: String块请,或者在構(gòu)建Body的JsonBean里加一個token的屬性。那能不能在retrofit的基礎(chǔ)上進(jìn)行擴(kuò)展簡單的完成這件事呢晕讲。

需求:通過自定義注解實現(xiàn)請求參數(shù)的注入

這里涉及兩個問題覆获,自定義注解的解析,請求參數(shù)的注入瓢省。

我們都知道retrofit是通過動態(tài)代理實現(xiàn)的弄息,那么第一個問題就好解決了,可以通過再一次的動態(tài)代理也就是雙重代理拿到方法注解的信息勤婚。

請求參數(shù)的問題摹量,我們可以通過Okhttp的攔截器來實現(xiàn)。GET的方式的請求添加參數(shù)到url結(jié)尾,POST及其他方式需要重構(gòu)RequestBody缨称。

自定義注解:

@SIGN 方法注解凝果,用于注解retrofit所代理的apiService的接口方法上

@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface SIGN {
    boolean value() default true;
}

雙重代理:

不多說直接上代碼:

T apiService = retrofit.create(tClass);
T newApiService = (T) newProxyInstance(tClass.getClassLoader(), new Class<?>[]{tClass}, new ProxyHandler(apiService));

上面代碼很簡單,tClass是被代理的接口睦尽,先調(diào)用retrofit的動態(tài)代理生成代理類器净,再調(diào)用一次動態(tài)代理,動態(tài)代理的邏輯實現(xiàn)在ProxyHandler骂删。

ProxyHandler是一個實現(xiàn)InvocationHandler的類掌动,我們需要在實現(xiàn)方法invoke里解析我們自定義的注解@SIGN 。

下面是invoke方法的核心代碼:

 SIGN signAnnotation = method.getAnnotation(SIGN.class);
        boolean isSign = signAnnotation !=null && signAnnotation.value();
        if(!isSign){return;}
        String url = null;
        for(Annotation annotation: method.getDeclaredAnnotations()){
            if(annotation instanceof GET){
                url = ((GET) annotation).value();
            }else if(annotation instanceof POST){
                url = ((POST) annotation).value();
            }else if(annotation instanceof PUT){
                url = ((PUT) annotation).value();
            }else if(annotation instanceof DELETE){
                url = ((DELETE) annotation).value();
            }
        }
        //解析@Url
        if(TextUtils.isEmpty(url)){
            Annotation[][] annotations = method.getParameterAnnotations();
            for(int i=0; i<annotations.length;i++){
                for(int j=0; j<annotations[i].length;j++){
                    if(annotations[i][j] instanceof Url){
                        try {
                            String path = new URL((String)args[i]).getPath();
                            url = path.substring(1);
                        } catch (MalformedURLException e) {
                            e.printStackTrace();
                        }
                        break;
                    }
                }
                if(!TextUtils.isEmpty(url)) break;
            }
        }
        if(!TextUtils.isEmpty(url)){
            Timber.d(url);
            SignUrlSet.INSTANCE.add(url);
        }

我們先解析了方法是否被@SIGN標(biāo)記宁玫,若存在則解析該接口的Url路徑粗恢。(這里我沒有使用全路徑,需要全路徑的請傳入baseUrl)

在解析Url的時候我們并沒有使用反射去通過retrofit來獲取欧瘪,感興趣的同學(xué)可以嘗試一下眷射,這里不做贅述。

因為聯(lián)網(wǎng)請求的異步特性存urlPath集合我使用了CopyOnWriteArraySet佛掖,避免出現(xiàn)線程安全問題:

public enum SignUrlSet {
    INSTANCE;
    private Set<String> signUrls = new CopyOnWriteArraySet<>();

    public void add(String url){
        signUrls.add(url);
    }

    public boolean contains(String url){
        return signUrls.contains(url);
    }
}

Okhttp攔截器:

先看代碼(這個類kotlin代碼):

object SignInterceptor : Interceptor {

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {

        var request = chain.request()

        val path = request.url().encodedPath().removePrefix("/")
        if (SignUrlSet.INSTANCE.contains(path)) {
            if ("GET".equals(request.method())) {
                val httpUrl = request.url().newBuilder()
                        .addQueryParameter("token", UserRepository.mToken)
                        .build()
                request = request.newBuilder()
                        .url(httpUrl).build()
            } else {
                val requestBody = request.body()
                if (requestBody != null) {
                    val buffer = Buffer()
                    requestBody.writeTo(buffer)
                    val charset = requestBody.contentType()?.charset() ?: Charset.forName("UTF-8")
                    val bodyContent = buffer.readString(charset)
                    val newBodyContent = Gson().fromJson(bodyContent, JsonObject::class.java)
                            .apply { addProperty("token", UserRepository.mToken) }
                            .toString()
                    request = request.newBuilder()
                            .method(request.method(), RequestBody.create(requestBody.contentType(), newBodyContent))
                            .build()
                    Timber.d(newBodyContent)
                }
            }
        }

        return chain.proceed(request)
    }
}

我們在攔截器里拿到request,先通過從request解析出path路徑妖碉,再去SignUrlSet查找是否存在該urlPath,也就是該請求方法是否被@SIGN標(biāo)記芥被。

再通過處理GET方式的Url和其他方式的RequestBody欧宜,來build一個新的請求。

自此我們就完成了只通過一個注解就可以實現(xiàn)token參數(shù)的注入:

GET方式:

@SIGN
@GET("base/u/user")
fun getPersonalInfo(): Observable>

POST以及其他方式:

data class ModifyLangReq(var language: String)

@SIGN
@PUT("base/u/language")
fun changeLanguage(@Body reqBody: ModifyLangReq): Observable<BaseResponse<Any>>

以上我們就成功的給Retrofit擴(kuò)展了注入token的功能拴魄,其實通過這個方式還可以添加很多你需要的功能冗茸,比如很多業(yè)務(wù)需要用到的Token自動刷新、Token過期退出登錄匹中、加密夏漱、緩存、API版本號顶捷、多BaseUrl挂绰,都可以這樣實現(xiàn),有機(jī)會再和大家分享服赎。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末葵蒂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子重虑,更是在濱河造成了極大的恐慌践付,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嚎尤,死亡現(xiàn)場離奇詭異荔仁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門乏梁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來次洼,“玉大人,你說我怎么就攤上這事遇骑÷艋伲” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵落萎,是天一觀的道長亥啦。 經(jīng)常有香客問我,道長练链,這世上最難降的妖魔是什么翔脱? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮媒鼓,結(jié)果婚禮上届吁,老公的妹妹穿的比我還像新娘。我一直安慰自己绿鸣,他們只是感情好疚沐,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著潮模,像睡著了一般亮蛔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上擎厢,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天究流,我揣著相機(jī)與錄音,去河邊找鬼锉矢。 笑死梯嗽,一個胖子當(dāng)著我的面吹牛齿尽,可吹牛的內(nèi)容都是我干的沽损。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼循头,長吁一口氣:“原來是場噩夢啊……” “哼绵估!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起卡骂,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤国裳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后全跨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缝左,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了渺杉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛇数。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖是越,靈堂內(nèi)的尸體忽然破棺而出耳舅,到底是詐尸還是另有隱情,我是刑警寧澤倚评,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布浦徊,位于F島的核電站,受9級特大地震影響天梧,放射性物質(zhì)發(fā)生泄漏盔性。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一呢岗、第九天 我趴在偏房一處隱蔽的房頂上張望纯出。 院中可真熱鬧,春花似錦敷燎、人聲如沸暂筝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽焕襟。三九已至,卻和暖如春饭豹,著一層夾襖步出監(jiān)牢的瞬間鸵赖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工拄衰, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留它褪,地道東北人。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓翘悉,卻偏偏與公主長得像茫打,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子妖混,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容