Kotlin使用Fuel发送POST请求时服务器无响应问题排查求助
问题
我在使用Fuel库向服务器发送POST请求时遇到异常:本次请求的特殊配置为通过Bearer Token完成授权,且请求内容类型为JSON,相关实现代码如下:
fun callApi(site:String, token: String):String { val(request, response, result) = Fuel.post(site) .authentication() .bearer(token) .jsonBody("{\"instrumentStatus\":\"INSTRUMENT_STATUS_UNSPECIFIED\"}") .responseString() return response.toString() }
我编写了一个接收请求路径、已知Bearer Token作为入参的函数,随后在MainActivity.kt中定义site、token两个变量,调用callApi函数并将返回数据传递给TextView进行展示,相关代码如下:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val token = "t.GtiJaNIjpWy-Iw4sFSFvIEQHdCxxQS7Nz1HHTP4tM3pmfrXPyP3LbqJnbJln2Lpv85BiMK-ry3ynhzFFVgJM_A" val site = "https://invest-public-api.tinkoff.ru/rest/tinkoff.public.invest.api.contract.v1.InstrumentsService/Bonds" val textView: TextView = findViewById(R.id.textView) textView.text = callApi(site,token) } }
我已通过Postman验证服务器接口可用性,Postman中配置完全相同的请求参数时可正常收到响应,不清楚代码为何无法拿到返回数据。
错误原因
你的代码存在3个直接导致请求失败/拿不到正确结果的问题:
- 主线程发起网络请求。Android API 11及以上版本默认禁止在UI主线程执行网络操作,你在
onCreate生命周期里直接调用同步阻塞的responseString()方法,会直接抛出NetworkOnMainThreadException,请求根本没有发出去。Postman作为桌面端程序没有Android的线程限制,所以可以正常请求。 - 缺少网络权限声明。Android应用发起网络请求必须在
AndroidManifest.xml中声明INTERNET权限,没有该权限所有网络操作都会被系统直接拦截。 - 返回值处理逻辑完全错误。首先
response.toString()返回的是Response类实例的对象字符串(格式类似com.github.kittinunf.fuel.core.Response@1a2b3c),根本不是接口返回的响应体内容;其次你完全没处理result中封装的请求异常,所有报错都被吞掉,无法定位问题。另外多余调用了.authentication()方法,这个方法是给Basic基础认证(用户名密码认证)用的,Bearer Token认证不需要调用,多余调用可能干扰请求头生成。
修复步骤
- 首先在
AndroidManifest.xml的<manifest>节点下(与<application>节点平级)添加网络权限:
<uses-permission android:name="android.permission.INTERNET" />
- 改造
callApi方法,使用Fuel的异步请求方式,移除多余的authentication调用,正确处理响应结果和异常,注意异步请求不能直接返回值,需要通过回调回传结果:
fun callApi(site: String, token: String, callback: (resultText: String) -> Unit) { Fuel.post(site) .bearer(token) .jsonBody("{\"instrumentStatus\":\"INSTRUMENT_STATUS_UNSPECIFIED\"}") .responseString { _, _, result -> when (result) { is Result.Success -> { // 从result中取真正的响应体内容 val responseContent = result.get() // 切回主线程更新UI Handler(Looper.getMainLooper()).post { callback(responseContent) } } is Result.Failure -> { val error = result.getException() error.printStackTrace() Handler(Looper.getMainLooper()).post { callback("请求出错:${error.message}") } } } } }
- 修改Activity中的调用逻辑,通过回调接收结果更新TextView:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val token = "t.GtiJaNIjpWy-Iw4sFSFvIEQHdCxxQS7Nz1HHTP4tM3pmfrXPyP3LbqJnbJln2Lpv85BiMK-ry3ynhzFFVgJM_A" val site = "https://invest-public-api.tinkoff.ru/rest/tinkoff.public.invest.api.contract.v1.InstrumentsService/Bonds" val textView: TextView = findViewById(R.id.textView) callApi(site, token) { result -> textView.text = result } } }
补充:你调用的接口是HTTPS协议,不需要额外配置明文流量权限,改完以上三点即可正常拿到响应。
内容的提问来源于stack exchange,提问作者Vladislav Ponomaryov
相关产品推荐
相关产品推荐

