CXF客户端乱码
解决办法一,设置服务端代码:
在使用CXF与其他系统对接时,发现对方系统响应的汉字乱码,使用soapui调用测试就没有问题,但是程序里面调用就乱码,很奇怪,乱码如下:
由于不知道问题所在,只能在CXF动态调用的客户端代码上加IN 和 OUT拦截器,查看请求和响应,代码如下:
//获取cxf客户端JaxWsDynamicClientFactory clientFactory = JaxWsDynamicClientFactory.newInstance();Client client = clientFactory.createClient(executeDataUrl);//一个是namespace 一个是调用的方法名 根据你自己的接口填写QName qName = new QName(executeNameSpace, executeDataMethod);String param2 = "10001";String param3 = "测试";//日志拦截器client.getInInterceptors().add(new LoggingInInterceptor());client.getOutInterceptors().add(new LoggingOutInterceptor());Object[] result = client.invoke(qName, token, param2, param3);System.out.println(result[0]);
然后发送,发现日志很清晰,如下:
发现什么了吗? 兄弟们,返回编码居然是ISO8859-1? 但是对方系统调查后说是返回的就是UTF-8编码,为啥我本地成了ISO-8859-1编码了?
于是我在本地创建了CXF服务端,再次使用客户端调用,发现日志如下:
发现什么了吗?老铁们,Content-Type: text/xml;charset=UTF-8 这个响应头和他们给我返回的不一样,那是不是必须设置响应头的Content-Type属性的字符集为UTF-8才行呢,于是我使用一个OUT过滤器解决了这个问题(我是springboot项目,其他工程也是一样),
CXF服务端添加OUT过滤器并设置响应头,过滤器代码如下:
package com.npcs.cxf.interceptor;import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.springframework.stereotype.Component;import java.util.*;/*** @author MPF* @date 2021/2/28 17:34* @desc*/@Component
public class ClientAuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {public ClientAuthInterceptor() {//准备发送阶段 如果不是这个阶段可能会获取不到内容哦 要注意super(Phase.PRE_STREAM);}@Overridepublic void handleMessage(SoapMessage message) throws Fault {Map<String, List<String>> headers =(Map<String, List<String>>)message.get(Message.PROTOCOL_HEADERS);if(headers == null){headers = new TreeMap<String,List< String>>(String.CASE_INSENSITIVE_ORDER);message.put(Message.PROTOCOL_HEADERS,headers);}headers.put("Content-Type", Arrays.asList("text/xml;charset=UTF-8"));}
}
服务端发布应用添加OUT过滤器:
package com.npcs.cxf.config;import com.npcs.cxf.interceptor.ClientAuthInterceptor;
import com.npcs.cxf.service.CxfTestService;
import org.apache.cxf.Bus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.xml.ws.Endpoint;@Configuration
public class CxfTestConfig {@Autowiredprivate ClientAuthInterceptor clientAuthInterceptor;@Autowiredprivate Bus bus;@Autowiredprivate CxfTestService cxfTestService;@SuppressWarnings("all")@Beanpublic ServletRegistrationBean dispServlet() {return new ServletRegistrationBean(new CXFServlet(), "/soap/*");}/*** JAX-WS* * 站点服务* ***/@Beanpublic Endpoint endpoint() {EndpointImpl endpoint = new EndpointImpl(bus, cxfTestService);endpoint.publish("/test");//添加OUT过滤器 你刚才写的过滤器就在这里添加endpoint.getOutInterceptors().add(clientAuthInterceptor);return endpoint;}
}
这样就可以了,每次响应回来的数据都是UTF-8编码的,客户端不会再乱码了。这里只是拿ISO8859-1做对比,其他环境可能GBK或其他编码,都可以通过设置Content-Type来解决。
注: 我这个只是java代码调用乱码,用soapui或postman调用不会乱码的(他们系统返回的字符集的确是UTF-8),很奇怪,我不知道如果代码不指定字符集,服务器会不会自动加什么,我在本地的测试案例不设置字符集也不乱码,和他们的区别就是我的是tomcat9, 他们的是jetty9。
解决办法二,设置客户端代码:
客户端代码修改同样需要设置拦截器,不过是设置客户端的 IN 拦截器,拦截器代码如下:
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.springframework.stereotype.Component;/*** @author 小兵* @date 2021/3/1 12:55* @desc CXF客户端 如果服务端的Content-Type没有指定字符集编码,客户端会使用ISO8859-1解析* 会出现乱码情况,此时需要设置一下客户端的解析编码 将非UTF-8的编码转换为UTF-8*/
@Component
public class EncodingInterceptor extends AbstractPhaseInterceptor<SoapMessage> {//指定字符集private static final String ENCODING_DEFAULT = "UTF-8";public EncodingInterceptor() {//准备发送阶段super(Phase.PRE_STREAM);}@Overridepublic void handleMessage(SoapMessage message) throws Fault {//不是UTF-8编码的替换成UTF-8String encoding = (String) message.get(Message.ENCODING);if(!ENCODING_DEFAULT.equals(encoding)){message.put(Message.ENCODING, ENCODING_DEFAULT);}}
}
在客户端代码中添加 IN 拦截器:
//获取cxf客户端JaxWsDynamicClientFactory clientFactory = JaxWsDynamicClientFactory.newInstance();Client client = clientFactory.createClient(executeDataUrl);QName qName = new QName(executeNameSpace, executeDataMethod);client.getInInterceptors().add(encodingInterceptor );Object[] result = client.invoke(qName, "测试");System.out.println(result[0]);
这样也不会乱码,结果如下: