1. 程式人生 > >Retrofit2應對各種奇葩介面的方法

Retrofit2應對各種奇葩介面的方法

最近這一年半,實在是大開眼界了,面對各種奇葩的介面,在緊湊的開發週期下,沒有時間細想如何去面對,好在最近稍微清閒了,就把遇到的各種奇葩介面整理了一下,自己手寫Spring去模擬這些介面,然後嘗試用Retrofit一一破解,終於被我摸出了一些門道。

Post請求上傳Json

首先說明一點,用Json做前後端互動其實是很好的做法,我個人也很推薦這樣來玩。

遇到這種場景,我們首先想到的就是百度,然後我們會了解以下寫法:

方法1:

@Headers("Content-Type: application/json;charset=UTF-8", "Accept: application/json")
@POST(ServerAPI.JPUSH_UPLOAD_DEVICE_TOKEN_INFO)

fun uploaddevicetoken(@Body body: RequestBody): Single<LaiKangJunVO<Any>>


//--------------------------
val gson = Gson()
val jsonJpushStr = gson.toJson(jpushload)
val requstBody = RequestBody.create(MediaType.parse("application/json"), jsonJpushStr)
val disposable = accountService.uploaddevicetoken(requstBody)
.subscribeOn(Schedulers.io())

這種方式並沒有錯,但是我們忽略了一點,我們添加了ConverterFactory。我們都知道,如果新增ConverterFactory,就可以直接這樣寫:

方法2:

@POST("test4")
fun test4(@Body test: Test):Single<Bean>

//----------------------
service.test4(Test("XXX", 11))
	.subscribeOn(Schedulers.io())
	.subscribe({
	val i = 0
	}, {
	val i = 0
	})

直接把物件當引數傳入即可,是不是方便很多?

傳遞空Json

有個弟弟寫過一個介面,讓我傳一個空的Json串,即“{}”,我先用上面的方法1來跑,不管怎麼寫retrofit都會報錯,時間緊沒辦法,我跑去找弟弟吵了一架,讓人家改了。但是後來反思,當哥的應該儘量相容弟弟,這個問題該怎麼解決呢,以後再遇到這問題該怎麼處理,直到我用上面的方法2傳遞了一個空的物件,終於解決了:

data class TestBean()

@POST("test4")
fun test(@Body bean: TestBean):Single<Bean>

//----------------------
service.test(TestBean())
	.subscribeOn(Schedulers.io())
	.subscribe({
	val i = 0
	}, {
	val i = 0
	})

介面只返回ResponseHeader

事情的起因是,在做「登出」介面時,後端弟弟在spring攔截器裡面去攔截引數,不滿足的話他不能返回responseBody,只能返回一個responseHeder,但是由於我們用了ConverterFactory,retrofit會自動把responseBody反序列化成物件,但此時的responseBody是個空串,所以在解析的時候就會報解析錯誤end of .....

應對這種場景,我們可以直接使用retrofit內部的okhttp來實現:

private fun ok() {
	val req = Request.Builder()
	.url(url + "test")
	.build()

	val moshi = Moshi.Builder().build()
	val call = client.newCall(req)
	call.enqueue(object : Callback {
	override fun onResponse(call: Call, response: Response) {
	val head = response.headers()
	val head1 = response.header("Date")
	val test = TestJsonAdapter(moshi).fromJson(response.body()?.source())
	val i = 0

	}

	override fun onFailure(call: Call, e: IOException) {
	val i = 0
	}
	})
}

但是這樣以來就造成了我們的程式碼的不統一。在我百般嘗試和各種百度後,終於找到一篇帖子,於是有了以下的解決方案:

@POST("test4")
    fun test4(@Body test: Test):Single<retrofit2.Response<Boolean>>
	
//---------------------------------

private fun retrofit4():Disposable {
	return service.test4(Test("XXX", 11))
	.subscribeOn(Schedulers.io())
	.subscribe({
		val i = 0
		val token = it.headers()["token"]
	}, {
		val i = 0
	})
}

登入後全域性加引數/動態切換全域性引數

我們都知道retrofit可以全域性在請求頭中加引數:

val client = OkHttpClient.Builder()
client.addInterceptor {
	val oriReq = it.request()
	val req = oriReq.newBuilder()
	.header("token", token.toString())
	.method(oriReq.method(), oriReq.body())
	.build()
	it.proceed(req)
}
retrofit = Retrofit.Builder()
	.baseUrl(url)
	.client(client.build())
	.addConverterFactory(MoshiConverterFactory.create())
	.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
	.build()
service = retrofit.create(Service::