最近这一年半,实在是大开眼界了,面对各种奇葩的接口,在紧凑的开发周期下,没有时间细想如何去面对,好在最近稍微清闲了,就把遇到的各种奇葩接口整理了一下,自己手写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>
//--------------------------
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
//----------------------
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
//----------------------
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>
//---------------------------------
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::class.java)