Retrofit创建URL时如何避免@QueryMap中&被编码为%26?
我完全懂你的困扰——当@QueryMap里的参数值自带"&"符号时,Retrofit默认的URL编码会把它转成"%26",导致接口请求的参数不符合预期,而且你试过常规的编码设置没搞定对吧?下面给你几个经过验证的可行方案:
方案一:用OkHttp拦截器手动修正URL(最推荐)
这个方法灵活且可控,能在请求发送前把被编码的"%26"替换回原始的"&",不影响其他参数的正常编码。
先创建一个拦截器:
OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request(); HttpUrl originalUrl = originalRequest.url(); // 检查查询字符串里是否有被编码的& String queryStr = originalUrl.query(); if (queryStr != null && queryStr.contains("%26")) { // 将%26替换回&,保留原始符号 String modifiedQuery = queryStr.replace("%26", "&"); // 构建新的URL HttpUrl modifiedUrl = originalUrl.newBuilder() .query(modifiedQuery) .build(); // 替换原请求的URL Request modifiedRequest = originalRequest.newBuilder() .url(modifiedUrl) .build(); return chain.proceed(modifiedRequest); } return chain.proceed(originalRequest); } }) .build();
然后把这个自定义的OkHttpClient传给Retrofit:
Retrofit retrofit = new Retrofit.Builder() .baseUrl(AppConstants.BASE_URL) .client(client) .addConverterFactory(GsonConverterFactory.create()) .build();
这样一来,所有经过这个client的请求,都会自动把查询参数里的"%26"还原成"&",完全不影响你的原有接口定义。
方案二:自定义URL编码器(针对性处理)
如果你只想对特定参数的&不编码,可以自定义一个Encoder,替换Retrofit的默认编码逻辑:
public class CustomQueryEncoder implements Encoder { private final Encoder defaultEncoder = new DefaultEncoder(); @Override public String encode(String input) throws UnsupportedEncodingException { // 先让默认编码器处理其他特殊字符,再把&的编码结果替换回来 String encoded = defaultEncoder.encode(input); return encoded.replace("%26", "&"); } }
然后在构建Retrofit的时候,通过@QueryMap的encoder参数指定这个自定义编码器:
@GET(AppConstants.BASE_URL + "{category_type}/") Call<JsonObject> callCustomFilterApI( @Path("category_type") String type, @QueryMap(encoded = true, encoder = CustomQueryEncoder.class) Map<String,String> fields, @Query("page") String pageNo );
不过要注意,这个方法会对整个@QueryMap里的所有参数生效,如果只有个别参数需要保留&,可能需要在自定义Encoder里加判断逻辑。
方案三:手动拼接URL(适合参数较少的场景)
如果你的参数数量不多,可以放弃@QueryMap,改用@Url注解手动拼接完整请求URL,这样能完全控制参数的格式:
@GET Call<JsonObject> callCustomFilterApI(@Url String fullUrl);
调用的时候手动组装URL:
// 示例参数 String categoryType = "books"; String pageNo = "2"; Map<String, String> fields = new HashMap<>(); fields.put("filter", "price>10&stock>0"); // 这里的&直接保留 // 手动拼接查询参数 StringBuilder queryBuilder = new StringBuilder(); queryBuilder.append("page=").append(pageNo); for (Map.Entry<String, String> entry : fields.entrySet()) { queryBuilder.append("&").append(entry.getKey()).append("=").append(entry.getValue()); } String fullUrl = AppConstants.BASE_URL + categoryType + "/" + "?" + queryBuilder.toString(); // 发起请求 yourApiService.callCustomFilterApI(fullUrl);
这个方法的好处是完全可控,但要注意除了需要保留的&之外,其他特殊字符(比如空格、中文)还是需要手动编码,避免出现URL格式错误。
最后提醒一句:要确保后端接口能正确解析这种带&的参数值——如果你的参数值里的&是用来表示多个子条件(比如filter=price>10&stock>0),后端需要能把整个字符串当成一个参数值来处理,而不是把stock>0当成另一个独立参数。
内容的提问来源于stack exchange,提问作者Ankur_009

