在接受soap消息的时候,可能需要对消息做预处理!可以使用拦截器实现这一目的!
cxf有2种拦截器,InInterceptor、OutInterceptor,顾名思义,InInterceptor可以处理soap请求消息,OutInterceptor可以处理soap响应消息。
所有的拦截器都继承自AbstractPhaseInterceptor<?>,此抽象拦截器实现了Interceptor接口!
可以通过注解和配置文件2种方式来启用自定义的拦截器。
先来看一个InInterceptor
package XXX.web.webservice.interceptor; import java.io.ByteArrayInputStream; import java.io.InputStream; import org.apache.cxf.message.Message; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; import org.apache.log4j.Logger; public class ArtifactInInterceptor extends AbstractPhaseInterceptor<Message> { private static final Logger log = Logger.getLogger(ArtifactInInterceptor.class); public ArtifactInInterceptor() { //这儿使用receive,接收的意思 super(Phase.RECEIVE); } public void handleMessage(Message message){ try { InputStream is = message.getContent(InputStream.class); //这里可以对流做处理,从流中读取数据,然后修改为自己想要的数据 //处理完毕,写回到message中 //在这里需要注意一点的是,如果修改后的soap消息格式不符合webservice框架格式,比如:框架封装后的格式为 //<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope" <soap:Body> //<这里是调用服务端方法的命名空间><这是参数名称> //这里才是真正的消息 //</这里是调用服务端方法的命名空间></这是参数名称> //</soap:Body> //</soap:Envelope> //如果是以上这种格式,在暴露的接口方法里才会真正接收到消息,而如果请求中在body里边,没有加方法命名空间和参数名称,直接就是消息体 //那接口方法里是接收不到消息的,因为cxf是按照上面的这种格式去解析的,所以如果不符合上面的格式,就应该在这里做处理 //……………………处理代码…………………… if(is != null) message.setContent(InputStream.class, is); } catch (Exception e) { log.error("Error when split original inputStream. CausedBy : "+"\n"+e); } } }
然后使用注解@InInterceptors(interceptors="XXX.ArtifactInInterceptor")加到实现类上。
InInterceptor相比OutInterceptor来说简单多了,我在使用OutInterceptor的时候,始终从流里边读不到数据,官网上没有例子,网上也搜了好多好多,最后在一个国外的论坛上搜到了,使用方式如下:
package XXX.web.webservice.interceptor; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.commons.io.IOUtils; import org.apache.cxf.io.CachedOutputStream; import org.apache.cxf.message.Message; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; import org.apache.log4j.Logger; public class ArtifactOutInterceptor extends AbstractPhaseInterceptor<Message>{ private static final Logger log = Logger.getLogger(ArtifactOutInterceptor.class); public ArtifactOutInterceptor() { //这儿使用pre_stream,意思为在流关闭之前 super(Phase.PRE_STREAM); } public void handleMessage(Message message) { try { OutputStream os = message.getContent(OutputStream.class); CachedStream cs = new CachedStream(); message.setContent(OutputStream.class, cs); message.getInterceptorChain().doIntercept(message); CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class); InputStream in = csnew.getInputStream(); String xml = IOUtils.toString(in); //这里对xml做处理,处理完后同理,写回流中 IOUtils.copy(new ByteArrayInputStream(xml.getBytes()), os); cs.close(); os.flush(); message.setContent(OutputStream.class, os); } catch (Exception e) { log.error("Error when split original inputStream. CausedBy : " + "\n" + e); } } private class CachedStream extends CachedOutputStream { public CachedStream() { super(); } protected void doFlush() throws IOException { currentStream.flush(); } protected void doClose() throws IOException { } protected void onWrite() throws IOException { } } }
然后使用注解@OutInterceptors(interceptors="XXX.ArtifactOutInterceptor")加到实现类上。
cxf框架中,有默认的拦截器链,可以使用addAfter()和addBefore()方法来指定自己的拦截器放到什么位置。