使用Retrofit通过PUT请求发送Enum时出现HTTP 400错误排查
问题原因及解决方案
核心问题:请求体格式与API要求不匹配
你直接将TaskState枚举作为@Body参数发送,Gson默认会把它序列化成纯字符串(比如"NOT_ASSIGNED"),但目标API的editTaskState接口要求的是包含指定字段的JSON对象(而非纯字符串),同时可能还要求枚举值使用小写蛇形命名(比如not_assigned),这两点都不符合API的格式规范,因此返回HTTP 400错误。
具体解决方案
1. 修正请求体结构:创建包装类
API期望的请求体是类似这样的JSON结构:
{ "state": "not_assigned" }
你需要创建一个包装类来匹配这个结构:
data class TaskStateRequest( val state: TaskState )
然后修改Retrofit的接口方法,用这个包装类作为请求体:
@PUT("/project/76fd04cc-ae1e-4589-8fe7-11c4f16ce067/task/{taskId}/state/") suspend fun updateState( @Path("taskId") taskId: String, @Body stateRequest: TaskStateRequest )
调用时传入TaskStateRequest(TaskState.NOT_ASSIGNED)即可。
2. 修正枚举值的序列化格式(适配API命名要求)
如果API要求枚举值是小写蛇形(比如not_assigned而非NOT_ASSIGNED),需要给Gson添加自定义枚举适配器,统一转换格式:
class TaskStateAdapter : TypeAdapter<TaskState>() { override fun write(out: JsonWriter, value: TaskState) { val apiValue = when(value) { TaskState.NOT_ASSIGNED -> "not_assigned" TaskState.IN_PROGRESS -> "in_progress" TaskState.CLOSED -> "closed" TaskState.DELETED -> "deleted" } out.value(apiValue) } override fun read(`in`: JsonReader): TaskState { val value = `in`.nextString() return when(value) { "not_assigned" -> TaskState.NOT_ASSIGNED "in_progress" -> TaskState.IN_PROGRESS "closed" -> TaskState.CLOSED "deleted" -> TaskState.DELETED else -> throw IllegalArgumentException("Unknown task state: $value") } } }
然后在构建Gson实例时注册这个适配器:
val gson = GsonBuilder() .registerTypeAdapter(TaskState::class.java, TaskStateAdapter()) .create()
最后将这个Gson传给Retrofit的转换器工厂:
Retrofit.Builder() .baseUrl("你的API基础地址") .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create(gson)) .build()
3. 验证实际请求内容(可选但推荐)
添加OkHttp的日志拦截器,打印出实际发送的请求体,对比API文档要求能快速定位问题:
val loggingInterceptor = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } val okHttpClient = OkHttpClient.Builder() .addInterceptor(loggingInterceptor) .build()
将这个okHttpClient配置到Retrofit中,就能在日志里看到完整的请求内容,确认是否符合API规范。
内容的提问来源于stack exchange,提问作者mikolaj-jalocha
相关产品推荐
相关产品推荐

