我需要将freshCsrf
字符串添加到每个请求到表单体中。OkHttpClient
和AuthInterceptor
共用。问题是如何在拦截器内发送请求以获得freshCsrf
字符串?
下面的代码出现错误
Suspend function 'getAuth' should be called only from a coroutine or another suspend function
class AuthInterceptor(val authApi: IAuthService) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
val freshCsrf = authApi.getAuth().freshCsrf
// ... add freshCsrf to form body
return chain.proceed(request)
}
}
// Retrofit service
interface IAuthApi {
@GET("/auth")
suspend fun getAuth(): Auth
}
我尝试使用协程,但也失败了,因为不能等待结果。例子之一async/await
了错误
Suspend function 'await' should be called only from a coroutine or another suspend function
private val scope = CoroutineScope(Dispatchers.IO)
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val freshCsrf = scope.async { authApi.getAuth().freshCsrf }
freshCsrf.await()
// ... add freshCsrf to form body
return chain.proceed(request)
}
更新1
我混淆了runBlocking
的描述
设计用于桥接类库的常规阻塞代码挂起式编写,用于主函数而在测试.
一方面,改装接口暂停,我需要等待网络结果,以便继续创建其他请求(桥接- ok)。但在另一方面,它不是主函数或测试。许多教程和文章都告诉我们必须在产品代码中避免runBlocking
。你能解释一下吗?
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val freshCsrf = runBlocking {
return@runBlocking authApi.getAuth().freshCsrf
}
// ... add freshCsrf to form body
return chain.proceed(request)
}
使用.execute()
代替.enqueue()
,它将同步执行请求。不需要Interceptor
,因为CSRF保护通常驻留在html<head>
中;
如果字符串将在HTTP头中提供,则这将是另一回事。
相关文档:Interface Call<T>
.
如果你不想使用runBlocking
,那么你将不得不在IAuthApi接口中引入第二个返回Call<Auth>
的端点。Call<Auth>
将允许您在其上运行execute()
函数以同步(阻塞)方式运行请求。
调用接口文档
下面使用Call.execute
:
class AuthInterceptor(val authApi: IAuthService) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
val freshCrsf = authApi.getAuthCall().execute().let { authResponse ->
if(authResponse.isSuccessful) {
authResponse.body().freshCrsf
} else {
// TODO handle error for example you can read authResponse.errorBody()
// and do something based on it
}
}
// ... add freshCsrf to form body
return chain.proceed(request)
}
}
// Retrofit service
interface IAuthApi {
@GET("/auth")
fun getAuthCall(): Call<Auth>
@GET("/auth")
suspend fun getAuth(): Auth
}
我认为runBlocking
在这种情况下真的不是一个坏的选择。作为文档状态用于桥暂停代码的阻塞。Main函数和测试对我来说是常见的用例。
interface ApiInterface {
@GET("user/list")
fun userListGet(): Call<UserPageResponse>
}
lifecycleScope.launch {
val response = apiInterface.userListGet().await()
}