如何在Retrofit的GET请求中发送对象参数?

12
我有一个后端服务器,它的工作方式如下
"api/videos?search_object={"cat_id" :2, "channel_id" : 3, etc}

你可以将一个搜索对象作为输入,然后它会基于该对象对列表进行过滤。现在我想使用 Retrofit 来使用此服务,类似于以下内容:

Basily你可以给一个搜索对象作为输入,它将根据该对象过滤列表。 现在我想使用Retrofit与类似这样的东西


@GET("videos")
Call<VideoListResponse> listVideos(@Query("search_object") VideoSearchObject videoSearchObject);

但是上面的代码不起作用,我可以先将VideoSearchModel转换为JSON字符串,然后将其传递给Retrofit,像这样:

@GET("videos")
Call<VideoListResponse> listVideos(@Query("search_object") String jsonString);

我想知道是否有更好、更清晰的方式?欢迎提出任何建议。


如果您想在查询中发送JSON字符串,恐怕这是唯一的方法。 - Ashwani K
3个回答

8
Retrofit 2支持此功能。您只需实现一个自定义转换器工厂,并覆盖stringConverter()方法即可。
考虑以下带有自定义注释的Retrofit友好接口:
@Target(PARAMETER)
@Retention(RUNTIME)
@interface ToJson {
}

interface IService {

    @GET("api/videos")
    Call<Void> get(
            @ToJson @Query("X") Map<String, Object> request
    );

}

注解用于表示必须转换为字符串的参数。

模拟 OkHttpClient 总是响应“HTTP 200 OK”并转储请求URL:

private static final OkHttpClient mockHttpClient = new OkHttpClient.Builder()
        .addInterceptor(chain -> {
            System.out.println(chain.request().url());
            return new Response.Builder()
                    .request(chain.request())
                    .protocol(HTTP_1_0)
                    .code(HTTP_OK)
                    .body(ResponseBody.create(MediaType.parse("application/json"), "OK"))
                    .build();
        })
        .build();

private static final Gson gson = new Gson();

private static final Retrofit retrofit = new Retrofit.Builder()
        .client(mockHttpClient)
        .baseUrl("http://whatever")
        .addConverterFactory(new Converter.Factory() {
            @Override
            public Converter<?, String> stringConverter(final Type type, final Annotation[] annotations, final Retrofit retrofit) {
                if ( !hasToJson(annotations) ) {
                    return super.stringConverter(type, annotations, retrofit);
                }
                return value -> gson.toJson(value, type);
            }

            private boolean hasToJson(final Annotation[] annotations) {
                for ( final Annotation annotation : annotations ) {
                    if ( annotation instanceof ToJson ) {
                        return true;
                    }
                }
                return false;
            }
        })
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build();

要测试它,您可以简单地调用服务接口方法:
final IService service = retrofit.create(IService.class);
service.get(ImmutableMap.of("k1", "v1", "k2", "v2")).execute();

结果:

http://whatever/api/videos?X={%22k1%22:%22v1%22,%22k2%22:%22v2%22}

X 参数的取值是编码过的 {"k1":"v1","k2":"v2"} 表示。


1
您可以尝试下面的代码,它对我有效。
@GET("api")
Call<Response> method(@Query("") JSONObject param);

Map<String, String> map = new HashMap<>();
map.put("key", "value");
JSONObject json = new JSONObject(map);

0
"api/videos?search_object={"cat_id" :2, "channel_id" : 3, etc}

Basically you can give a search object as input

不,您不需要将对象作为输入。您提供多个参数,用 { } 括起来,以便它看起来像一个对象(JavaScript 对象,而不是 Java 对象)。实际上它只是一个字符串。

构建的 URL 只是一堆字符。在 URL 中不存在“对象”这样的东西。

继续使用 @Query("search_object") String jsonString。虽然您可能还想将参数从 jsonString 重命名为 searchString,因为它就是搜索字符串。它不是 JSON 字符串。JSON 字符串应该将所有的 " 字符转义,如 \"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接