如何在触发WebServiceException前拦截含无效字符的SOAP响应XML?
解决方案:在XML解析前过滤无效字符
你的问题核心在于:JAX-WS客户端的XML解析器(此处为Woodstox)会在读取响应流的第一时间校验字符合法性,而各类拦截器/处理器都是在解析完成后才介入,所以异常会提前触发。要解决这个问题,必须在流被解析之前就过滤掉无效字符,用FilterInputStream是完全可行的方案,以下是具体实现步骤(以Apache CXF为例,SpringBoot集成WebService常用CXF):
1. 实现自定义输入流过滤器
创建一个继承FilterInputStream的类,在读取字节时过滤掉0x1a(或其他XML无效字符):
import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; public class InvalidCharFilterStream extends FilterInputStream { protected InvalidCharFilterStream(InputStream in) { super(in); } @Override public int read() throws IOException { int ch; // 循环读取,跳过0x1a字符 do { ch = super.read(); } while (ch == 0x1A); return ch; } @Override public int read(byte[] b, int off, int len) throws IOException { int bytesRead = super.read(b, off, len); if (bytesRead == -1) { return -1; } int validPos = off; // 遍历字节数组,移除0x1a字符 for (int i = off; i < off + bytesRead; i++) { if (b[i] != 0x1A) { b[validPos++] = b[i]; } } // 返回有效字节数 return validPos - off; } }
2. 自定义HTTP传输管道
替换CXF默认的HTTPConduit,让它返回我们的过滤流:
import org.apache.cxf.transport.http.HTTPConduit; import org.apache.cxf.transport.http.HTTPURLConnection; import org.apache.cxf.Bus; import org.apache.cxf.endpoint.EndpointInfo; import org.apache.cxf.transport.ConduitInitiator; import java.io.IOException; import java.io.InputStream; public class CustomHTTPConduit extends HTTPConduit { public CustomHTTPConduit(ConduitInitiator initiator, Bus bus, EndpointInfo targetInfo) { super(initiator, bus, targetInfo); } @Override protected InputStream getInputStream(HTTPURLConnection connection) throws IOException { // 获取原始输入流,包装成过滤流返回 InputStream originalStream = super.getInputStream(connection); return new InvalidCharFilterStream(originalStream); } }
3. 配置WebService客户端使用自定义管道
在创建客户端代理时,替换默认的传输管道:
import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; import java.net.URL; public class PolizaClient { public static void main(String[] args) throws Exception { JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); factory.setServiceClass(PolizaSoap.class); factory.setAddress("http://example.org.co/your-webservice-endpoint"); // 创建客户端代理 PolizaSoap polizaSoap = (PolizaSoap) factory.create(); // 获取CXF客户端实例,替换管道 Client client = ClientProxy.getClient(polizaSoap); HTTPConduit originalConduit = (HTTPConduit) client.getConduit(); CustomHTTPConduit customConduit = new CustomHTTPConduit( originalConduit.getConduitInitiator(), client.getBus(), client.getEndpoint().getEndpointInfo() ); client.setConduit(customConduit); // 现在调用方法就不会触发无效字符异常了 ArrayOfPolizas result = polizaSoap.Polizas("param1", "param2", "param3"); } }
关键说明
- 为什么之前的拦截器无效?因为
SOAPHandler、ClientInterceptor等组件都是在XML解析完成、SOAP消息已经被封装成Java对象后才执行,而Woodstox解析器在读取流的阶段就会校验字符,异常触发在这些拦截逻辑之前。 - 如果使用的是Metro(Oracle的JAX-WS实现),核心思路依然是替换输入流,但需要通过自定义
TransportPipe来实现,步骤类似。
内容的提问来源于stack exchange,提问作者Olivetz
相关产品推荐
相关产品推荐

