You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

咨询Retrofit 2中GET请求发送JSON的优雅实现,当前代码失效

解决Retrofit 2中GET请求发送JSON的优雅方式

嘿,这个坑我之前踩过!Retrofit 本身默认不允许给 GET 请求加 @Body 注解——毕竟 HTTP 规范里本来就没定义 GET 请求带请求体的场景,所以你那代码跑不起来太正常了。下面给你几个优雅的解决思路,按需选就行:

1. 将JSON转为字符串作为查询参数传递

这是最简单的方案,把你的 SearchRequest 对象序列化成 JSON 字符串,然后通过 @Query 作为查询参数传递给后端:

接口定义

@GET("products")
fun getProducts(@Query("search") searchJson: String): Call<List<Product>>

调用示例

用 Gson 或者 Moshi 把请求对象转成字符串:

val gson = Gson()
val searchRequest = SearchRequest(/* 你的参数 */)
val searchJson = gson.toJson(searchRequest)
apiService.getProducts(searchJson).enqueue(/* 你的回调 */)

⚠️ 注意:这种方式受限于 URL 的长度限制,如果你的 JSON 内容太长,可能会触发后端或者浏览器的长度限制错误。

2. 用OkHttp Interceptor手动给GET请求加请求体

OkHttp 本身是支持 GET 请求带请求体的,只是 Retrofit 做了限制。我们可以写一个自定义拦截器,给特定的 GET 请求加上请求体:

第一步:定义标记注解

先写一个注解,用来标记哪些 GET 请求需要带请求体:

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class GetWithBody

第二步:实现拦截器

class GetWithBodyInterceptor(private val gson: Gson) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        // 检查当前请求是否标记了GetWithBody注解
        val invocation = originalRequest.tag(Invocation::class.java) ?: return chain.proceed(originalRequest)
        val hasAnnotation = invocation.method().isAnnotationPresent(GetWithBody::class.java)
        
        if (hasAnnotation) {
            // 从请求Tag中获取SearchRequest对象
            val searchRequest = originalRequest.tag(SearchRequest::class.java) ?: return chain.proceed(originalRequest)
            // 序列化为JSON请求体
            val requestBody = RequestBody.create(
                MediaType.parse("application/json; charset=utf-8"),
                gson.toJson(searchRequest)
            )
            // 构建新的GET请求,带上请求体
            val newRequest = originalRequest.newBuilder()
                .method("GET", requestBody)
                .build()
            return chain.proceed(newRequest)
        }
        return chain.proceed(originalRequest)
    }
}

第三步:配置Retrofit

把拦截器添加到 OkHttpClient 中:

val gson = Gson()
val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(GetWithBodyInterceptor(gson))
    .build()

val retrofit = Retrofit.Builder()
    .baseUrl("你的基础URL")
    .client(okHttpClient)
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build()

第四步:定义接口

用 @GetWithBody 标记接口,并用 @Tag 传递请求对象:

@GetWithBody
@GET("products")
fun getProducts(@Tag search: SearchRequest): Call<List<Product>>

这种方式既保留了 GET 请求的语义,又能发送 JSON 请求体,适合后端必须要求 GET 且 JSON 内容较长的场景。

3. 换用POST请求(最省心的规范方案)

其实从 HTTP 规范的角度来说,带请求体的查询操作更适合用 POST 请求。如果后端能配合修改接口,直接把 @GET 改成 @POST,你的原代码就能正常工作了:

@POST("products")
fun getProducts(@Body search: SearchRequest): Call<List<Product>>

这是最符合规范、也最省心的方案,很多后端都会支持用 POST 来处理复杂的搜索查询。

内容的提问来源于stack exchange,提问作者Arsene Huot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 08:17:42