ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

soap webService通过拦截器修改请求报文和响应报文

2021-06-21 19:01:27  阅读:317  来源: 互联网

标签:拦截器 请求 报文 class import message soap String


Spring boot如何开发CXF 框架的Webservice服务,参考上篇《Springboot开发WebService服务端和客户端》

做这个webService服务是因为甲方项目是集成平台的,要求我们开发webService服务端接收他们统一推送的信息进行同步数据,现在的情况是,集成平台要求服务端的请求报文和响应报文必须按照他们的格式来。

修改请求报文

这是我的请求报文:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://server.dandelion.com">
   <soapenv:Header/>
   <soapenv:Body>
      <ser:getData>
         <!--Optional:-->
         <action>同步用户信息</action>
         <!--Optional:-->
         <msg>
         	<![CDATA[
			<user>
				<id>100123322</id>
				<name>蒲公英不是梦</name>
			</user>
			]]>
		</msg>
      </ser:getData>
   </soapenv:Body>
</soapenv:Envelope>

这是他们要求的请求报文:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header/>
   <soapenv:Body>
      <esb:getData xmlns:esb="mdm.standard.com">
         <!--Optional:-->
         <action>同步用户信息</action>
         <!--Optional:-->
         <msg>
         	<![CDATA[
			<user>
				<id>100123322</id>
				<name>蒲公英不是梦</name>
			</user>
			]]>
		</msg>
      </esb:getData>
   </soapenv:Body>
</soapenv:Envelope>

区别在于:

  • 去掉了根节点的命名空间信息
  • 方法名增加了命名空间并修改前缀ser为esb
  • 命名空间由“http://server.dandelion.com”修改为“mdm.standard.com”

其中修改命名空间简单,在webService接口那里修改注解信息即可,问题在于如何把他从根节点取消掉,然后显示在方法名上,并且修改前缀。

在网上找了一些答案,大部分是客户端自定义请求报文,或者内容不全,或者webservice服务端是spring框架开发的,通过修改自定义的xml文件来实现。

我尝试了几种方法最终都不能修改到请求的模版格式,本着情况急任务紧的事实,我就利用webservice的拦截器在请求未执行之前获取请求的消息体,暴力修改内容达到目的。

思路是这样的:

通过SoapUI看到的客户端报文,仍然是原来的请求报文,但是允许集成平台按照他们的格式修改请求报文。发送请求时,拦截器进行拦截,将请求报文修改为webService服务端原来的请求报文,在执行任务。

CXF框架提供了AbstractPhaseInterceptor抽象类,通过定义phase(阶段)来拦截不同阶段的请求。

请求拦截器

import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;

import java.io.ByteArrayInputStream;
import java.io.InputStream;

public class WsInInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

    public WsInInterceptor(String phase) {
        super(phase);
    }

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        try {
            // 从流中获取请求消息体并以字符串形式输出,注意IOUtils是cxf的包;
            String input = IOUtils.toString(message.getContent(InputStream.class), "UTF-8");
            // 如果内容不为空(第一次连接也会被拦截,此时input为空)
            if (StringUtils.isNotBlank(input)){
                // 修改请求消息体为webservice服务要求的格式
                input = input.replace("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">","<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ser=\"http://server.dandelion.com\">")
.replace("<esb:getData xmlns:esb=\"mdm.stardand.com\">","<ser:getData>")
.replace("</esb:getData>", "</ser:getData>");
            }
            // 重新写入
            message.setContent(InputStream.class, new ByteArrayInputStream(input.getBytes()));
        } catch (Exception e) {
            System.out.println(String.format("解析报文异常: %s", e.getMessage()));
        }
    }
}

创建WsInInterceptor类继承AbstractPhaseInterceptor抽象类,需要创建构造方法传入phase参数并重写handleMessage方法。

首先通过message.getContent(InputStream.class)从流中获取请求报文。

我这边构造参数传入的是Phase.RECEIVE,所以通过SoapUI连接webService服务端时也会拦截请求,只不过请求报文为空字符串,所以需要进行判空。

将根节点的命名空间去掉

将方法名的命名空间去掉并修改前缀为原来的

然后重新写入

注入拦截器

在发布接口的配置文件中注入拦截器,并设置phase为receive阶段。

通过SoapUI测试效果:

打印效果:

修改响应报文

这是我的响应报文:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <ns2:getDataResponse xmlns:ns2="http://server.dandelion.com">
         <ns2:return>
             <![CDATA[
				<root>
					<code>1</code>
					<msg>同步成功</msg>
				</root>
			]]>
          </ns2:return>
      </ns2:getDataResponse>
   </soap:Body>
</soap:Envelope>

这是他们要求的响应报文:

<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"  xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
   <soapenv:Body>
      <return>
          <![CDATA[
			<root>
				<code>1</code>
				<msg>同步成功</msg>
			</root>
		  ]]>
      </return>
   </soapenv:Body>
</soapenv:Envelope>

同样是利用webservice的拦截器在结果未返回之前进行拦截,暴力修改响应报文达到目的。

响应拦截器

import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.phase.AbstractPhaseInterceptor;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class WsOutInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

    public WsOutInterceptor(String phase) {
        super(phase);
    }

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        try {
            // 从流中获取返回内容
            OutputStream os = message.getContent(OutputStream.class);
            CachedStream cs = new CachedStream();
            message.setContent(OutputStream.class, cs);
            message.getInterceptorChain().doIntercept(message);
            CachedOutputStream cachedOutputStream = (CachedOutputStream) message.getContent(OutputStream.class);
            InputStream in = cachedOutputStream.getInputStream();
            String output = IOUtils.toString(in, "UTF-8");
            // 修改内容为集成平台要求的格式
            output = output.replace("<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">","<soapenv:Envelope xmlns:soapenv=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\">")
.replace("</soap:Envelope>", "</soapenv:Envelope>")
.replace("<soap:Body>", "<soapenv:Body>")
.replace("</soap:Body>", "</soapenv:Body>")
.replace("<ns2:getDataResponse xmlns:ns2=\"http://server.dandelion.com\">", "")
.replace("</ns2:getDataResponse>", "");
            // 处理完后写回流中
            IOUtils.copy(new ByteArrayInputStream(output.getBytes()), os);
            cs.close();
            os.flush();
            message.setContent(OutputStream.class, os);
        } catch (Exception e) {
            System.out.println(String.format("解析报文异常: %s", e.getMessage()));
        }
    }

    private static class CachedStream extends CachedOutputStream {
        public CachedStream() {
            super();
        }
        @Override
        protected void doFlush() throws IOException {
            currentStream.flush();
        }
        @Override
        protected void doClose() throws IOException {
        }
        @Override
        protected void onWrite() throws IOException {
        }
    }
}

注入拦截器

在发布接口的配置文件中注入拦截器,并设置phase为pre_stream阶段。

通过SoapUI测试效果:

如果是按照响应结果中转义符转义失败,可能是接口的注解问题,化繁为简即可:

webservice接口:

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

@WebService(targetNamespace = "http://server.dandelion.com")
public interface TestService {

    @WebMethod(operationName="getData")
    String execute(@WebParam(name = "action") String action, @WebParam(name = "msg") String msg);
}

wbservice接口实现:

import javax.jws.WebService;

@WebService(targetNamespace = "http://server.dandelion.com",
        endpointInterface = "com.dandelion.server.TestService")
public class TestServiceImpl implements TestService{

    @Override
    public String execute(String action, String msg) {
        System.out.println(String.format("action: %s", action));
        System.out.println(String.format("msg: %s", msg));
        return "<root><code>1</code><msg>同步成功</msg></root>";
    }
}

标签:拦截器,请求,报文,class,import,message,soap,String
来源: https://www.cnblogs.com/dandelion200/p/14915182.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有