來源
近一段在開發接口平台,使用spring boot 開發,總體來說還是蠻順利;
接口平台不可避免的讓別人調用服務,也會去調用其它的外部接口;
突然有一天,某個服務商回調信息中出現了亂碼,形如:��ֵʧ��
,打破了寧靜
分析過程
向服務商確認,該接口傳遞的參數使用GBK編碼,WHT??現在還有用GBK的,不是用UTF8編碼么?
馬上確認我的程序編碼:
server.tomcat.uri-encoding 未設置
http.encoding.charset: UTF-8
http.encoding.force-request: true
可以看到uri的配置未定義,搜索文檔說明spring boot默認使用UTF8編碼
下面兩個是定義HTTP的字符集,用來設置request與response的字符集;
跟蹤源碼,發現這個字符參數會在CharacterEncodingFilter中用到,核心的方法是使用:request.setCharacterEncoding("UTF-8")
,並且是一個全局的內容
解決過程
將得到的亂碼用UTF-8編碼再用GBK解碼,如下代碼,得到結果為:
錕斤拷值失錕斤拷
new String(source.getBytes("UTF-8"),"GBK")
, 通過查資料得知,以GBK編碼->UTF-8解碼(出現亂碼)->UTF-8編碼(這步由於UTF8編碼的特殊性,會增加部分頭信息)->GBK解碼(由於加了特殊部分,所以造成不可逆的結果)在方法中調用
request.getCharacterEncoding("UTF-8")
無效,查詢rquest的這個方法說明,只有在獲取參數之前調用這個方法才有效,但在此之前spring 已幫我們做了很多事情,所以在這里無效;自已寫Filte來實現設置,但不知什么原因,還是無效
繼承
CharacterEncodingFilter
,對特定url做特殊處理,結果是自定義的encoding生效了,原先的UTF8的 encodingFilter失效了,就算修改自己的Bean名字還不行修改自定義EncodingFilter,實現多字符集的處理:
定義encodingFilter:
public class MutiCharacterEncodingFilter extends CharacterEncodingFilter implements Ordered{
//最高優先級
private int order = Integer.MIN_VALUE;
private List<String> mutiUrls = new ArrayList();
private String mutiCharset = "GBK";
public List<String> getMutiUrls() {
return mutiUrls;
}
public void setMutiUrls(List<String> mutiUrls) {
this.mutiUrls = mutiUrls;
}
public String getMutiCharset() {
return mutiCharset;
}
public void setMutiCharset(String mutiCharset) {
this.mutiCharset = mutiCharset;
}
/**
* @param charset
* @param mutiCharset
* @param mutiUrls
* @param forceRequest
* @param forceResponse
*/
public MutiCharacterEncodingFilter(String charset, String mutiCharset , List<String> mutiUrls, boolean forceRequest, boolean forceResponse) {
super(charset,forceRequest,forceResponse);
this.mutiUrls = mutiUrls;
this.mutiCharset = mutiCharset;
}
public MutiCharacterEncodingFilter() {
super();
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String path = request.getRequestURI();
//如果是這個鏈接 執行mutyEncoding方法
if(PathMatcherUtil.matchAny(mutiUrls,path)){
if (mutiCharset != null) {
if (isForceRequestEncoding() || request.getCharacterEncoding() == null) {
request.setCharacterEncoding(mutiCharset);
}
if (isForceResponseEncoding()) {
response.setCharacterEncoding(mutiCharset);
}
}
filterChain.doFilter(request,response);
return ;
}
//否則 使用默認方法
super.doFilterInternal(request,response,filterChain);
}
@Override
public int getOrder() {
return order;
}
在配置中設置相應內容:
http:
encoding:
charset: UTF-8
force-request: true
force-response: false
mutiCharset: GBK
mutiUrls:
- /notify/test
在config中生成實例
//第一種字符集
@Value(value = "${spring.http.encoding.charset}")
private String charset;
//是否強制應用到request
@Value(value = "${spring.http.encoding.force-request}")
private boolean forceRequest;
//是否強制設置到response
@Value(value = "${spring.http.encoding.force-response}")
private boolean forceResponse;
/**
* 自定義HTTP字符集 默認使用charset字符集 在特定情況下使用第二字符集 用於歐飛回調GBK編碼
* 僅對POST中的表單參數有效 對於URI中的參數無效 只能去修改tomcat的字符集編碼 應該不致於做的這么絕吧
* @return
*/
@Bean()
@ConfigurationProperties(prefix = "spring.http.encoding")
@ConfigurationPropertiesBinding
public MutiCharacterEncodingFilter mutiCharacterEncodingFilter(){
MutiCharacterEncodingFilter encodingFilter = new MutiCharacterEncodingFilter();
encodingFilter.setEncoding(charset);
encodingFilter.setForceRequestEncoding(forceRequest);
encodingFilter.setForceResponseEncoding(forceResponse);
return encodingFilter;
}
到此問題解決,可以接收GKB編碼的內容;
但同時也有缺陷,只適用於body的內容,不適用於鏈接到URL后面的請求參數,如果要支持,只能修改tomcat-uri-encoding為UTF-8,希望對大家有幫助.