对Retrofit + OkHttp还不熟悉的人可以点传送门,先看下这两个东西的使用。
- Retrofit: https://github.com/square/retrofit
- OkHttp: https://github.com/square/okhttp
分析
接口文档要求Post请求,字段使用GBK编码
我们先按照Retrofit的规范和接口文档来写接口:
@POST(SEND_API_URL) @FormUrlEncoded public Call<SendResponse> send(@FieldMapMap<String, Object> bodyMap);
Retrofit 默认情况下是使用UTF-8编码,其实就是调用的OkHttp3的编码规则。我们来看下OkHttp是怎么做的
//以下代码摘自okhttp3.HttpUrl static String canonicalize(String input, int pos, int limit, String encodeSet, boolean alreadyEncoded, boolean strict, boolean plusIsSpace, boolean asciiOnly) { int codePoint; for(int i = pos; i < limit; i += Character.charCount(codePoint)) { codePoint = input.codePointAt(i); if(codePoint < 32 || codePoint == 127 || codePoint >= 128 && asciiOnly || encodeSet.indexOf(codePoint) != -1 || codePoint == 37 && (!alreadyEncoded || strict && !percentEncoded(input, i, limit)) || codePoint == 43 && plusIsSpace) { Bufferout = new Buffer(); out.writeUtf8(input, pos, i); canonicalize(out, input, i, limit, encodeSet, alreadyEncoded, strict, plusIsSpace, asciiOnly); return out.readUtf8(); } } return input.substring(pos, limit); }
很多文章说,给他设置一个Content-Type,也就是带上Header,在做请求的时候,确实会去解析,但是对于编码来说,其实并没有什么用。
@Headers("Content-Type: application/x-www-form-urlencoded;charset=GBK")
解决方案
@POST(SEND_API_URL) @FormUrlEncoded @Headers("Content-Type: application/x-www-form-urlencoded;charset=GBK") public Call<SendResponse> send(@FieldMap(encoded = true) Map<String, Object> bodyMap);
在 @FieldMap
注解里面设置 encoded = true
这样做的意思就是,这个Map里面的东西我已经做过编码转换,不需要框架去重新转了。 也就是上面的代码中 alreadyEncoded = true
但是这里也要注意,在调用这个方法的时候,传入的Map要自己转码。
总结
最近在对接第三方接口,真的是千奇百怪,吐槽一天都吐槽不完。
作为开发人员,我觉得写对外的接口一定要注意,这个接口是给对方开发人员用的,至少编码、格式、参数加密、鉴权等等都要统一。
本是同根生相煎何太急 : )