使用Retrofit+Kotlin调用DialogFlow Post请求出现400错误求助
排查DialogFlow Retrofit请求400错误的问题
我帮你对比了Python的可用实现和你的Kotlin代码,找到了几个核心问题导致400 Bad Request错误,下面逐个说明并给出修复方案:
1. 请求体字段名不匹配(最关键问题)
你的Python代码里请求体的键是sessionId,但Kotlin的MsgBody类里定义的是sesId:
data class MsgBody(val lang: String, val query: String, val sesId: String)
Gson默认会把Kotlin字段名直接作为JSON键,所以发送的请求体里会是"sesId": "me",而DialogFlow的API明确期望"sessionId": "me",这直接导致请求格式不符合要求,触发400错误。
修复方案:
要么直接修改字段名,要么用@SerializedName注解指定JSON键:
// 方案1:直接改名(推荐) data class MsgBody(val lang: String, val query: String, val sessionId: String) // 方案2:保留原变量名,用注解映射JSON键 import com.google.gson.annotations.SerializedName data class MsgBody( val lang: String, val query: String, @SerializedName("sessionId") val sesId: String )
2. 响应模型字段名错误
DialogFlow的响应结构顶层字段是result,但你的Model.Response类里定义的是resp:
data class Response(val resp: Result)
这会导致Gson无法正确解析响应数据,即使请求成功也会抛出解析异常。
修复方案:
同样修改字段名或用注解映射:
// 方案1:直接改名 data class Response(val result: Result) // 方案2:用注解保留原变量名 data class Response(@SerializedName("result") val resp: Result)
3. Header的API KEY写法错误
你在@Headers里写了"Authorization: Bearer {MY API KEY}",这种写法是错误的——Retrofit的静态@Headers不支持占位符,这里会直接把{MY API KEY}作为字符串发送,导致鉴权失败。
修复方案:
如果API KEY是固定的,直接替换成实际的KEY字符串;如果需要动态传入,改用@Header注解:
// 静态固定KEY的写法 @Headers( "Authorization: Bearer 你的实际API KEY", "Content-Type: application/json" ) @POST("query") fun getAiMessage(@Body msg: Message.MsgBody, @Query("v") v: String): Observable<Model.Response> // 动态传入KEY的写法(更灵活,适合KEY需要动态配置的场景) @POST("query") fun getAiMessage( @Header("Authorization") auth: String, @Body msg: Message.MsgBody, @Query("v") v: String ): Observable<Model.Response> // 调用时传入示例: dialogFlowService.getAiMessage("Bearer 你的实际API KEY", Message.MsgBody("en",msg,"me"),"20170712")
4. 其他小细节修正
- 你的代码里
getAiMessage方法返回的是Observable<Model1.Response>,这里是笔误,应该改成Observable<Model.Response>。 - 建议添加OkHttp日志拦截器,方便查看实际发送的请求体和响应内容,快速定位问题:
// 先添加依赖:implementation 'com.squareup.okhttp3:logging-interceptor:4.10.0' val logging = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } val client = OkHttpClient.Builder().addInterceptor(logging).build() // 在Retrofit构建时添加客户端 val retrofit = Retrofit.Builder() .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .baseUrl("https://api.dialogflow.com/v1/") .client(client) .build()
修正后的完整代码示例
Message.kt
object Message { data class MsgBody( val lang: String, val query: String, val sessionId: String // 修正字段名 ) }
Model.kt
object Model { data class Response(val result: Result) // 修正字段名 data class Result(val fulfillment: Fulfillment) data class Fulfillment(val speech: String) }
DialogFlowService.kt
import io.reactivex.Observable import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.gson.GsonConverterFactory import retrofit2.http.Body import retrofit2.http.Header import retrofit2.http.POST import retrofit2.http.Query interface DialogFlowService { @POST("query") fun getAiMessage( @Header("Authorization") authToken: String, @Body msg: Message.MsgBody, @Query("v") apiVersion: String ): Observable<Model.Response> companion object { fun create(): DialogFlowService { val logging = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } val okHttpClient = OkHttpClient.Builder() .addInterceptor(logging) .build() val retrofit = Retrofit.Builder() .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .baseUrl("https://api.dialogflow.com/v1/") .client(okHttpClient) .build() return retrofit.create(DialogFlowService::class.java) } } }
MainActivity调用代码
private fun sendMessage(msg: String){ val authToken = "Bearer 你的实际API KEY" disposable = dialogFlowService.getAiMessage(authToken, Message.MsgBody("en",msg,"me"),"20170712") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { result -> textView.text = "DialogFlow says: ${result.result.fulfillment.speech}" // 修正响应字段路径 }, { error -> Toast.makeText(this, error.message, Toast.LENGTH_SHORT).show() } ) }
按照以上修改后,应该就能正确发送请求并获取DialogFlow的响应了。
内容的提问来源于stack exchange,提问作者mradey
相关产品推荐
相关产品推荐

