webservice测试工具-SoapUi
Apache CXF 是一个开源的 Services 框架,CXF 帮助您利用 Frontend 编程 API 来构建和开发 Services .
maven引用cxf:
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-rt-frontend-jaxwsartifactId>
<version>3.1.3version>
dependency>
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-rt-transports-httpartifactId>
<version>3.1.3version>
dependency>
启用cxf,web.xml文件中添加配置如下:
备注:webservice访问路径为:项目访问地址/ws/地址
<servlet>
<servlet-name>CXFServletservlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServletservlet-class>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>CXFServletservlet-name>
<url-pattern>/ws/*url-pattern>
servlet-mapping>
服务端发布webservice接口相关知识如下:
webservice接口(主要使用注解),示例代码如下:
接口(interface)
@WebService(targetNamespace = "urn:sinopec:ecc:fi:tmsno:fx")
public interface TmsImsWebService {
@WebMethod(operationName = "MT_TMS_ECC_FI_TMSYXNO")
public String tmsNo(@WebParam(name = "TmsYxNo") String tmsNo, @WebParam(name = "CODE") String compCode,
@WebParam(name = "YWRQ") String ywrq);
}
接口实现:
@WebService(endpointInterface = "com.pcitc.cbs.webService.TmsImsWebService", targetNamespace = "urn:sinopec:ecc:fi:tmsno:fx")
public class TmsImsWebServiceImpl implements TmsImsWebService {
@Autowired
private TmsConfirmRemainService tmsRemain;
@Override
public String tmsNo(String tmsNo, String compCode, String ywrq) {
String sss = tmsRemain.getTmsStrNo(tmsNo, compCode, ywrq);
return sss;
}
}
定义接口之后需要发布接口:配置文件如下
<bean id="TmsImsWebServiceImpl" class="com.pcitc.cbs.webService.impl.TmsImsWebServiceImpl">
bean>
<jaxws:endpoint id="TmsImsWebService" implementor="#TmsImsWebServiceImpl"
address="/tmsImsNo">
<jaxws:inInterceptors>
<ref bean="InMessageInterceptor" />
jaxws:inInterceptors>
jaxws:endpoint>
另一种发布方式:
<jaxws:server id="TmsImsWebService" address="/tmsImsNo"
serviceClass="com.pcitc.cbs.webService.TmsImsWebService">
<jaxws:serviceBean>
<bean class="com.pcitc.cbs.webService.impl.TmsImsWebServiceImpl" />
jaxws:serviceBean>
<jaxws:inInterceptors>
<ref bean="InMessageInterceptor" />
jaxws:inInterceptors>
jaxws:server>
备注:接口真正调用地址:
http://localhost:8080/fsscbs/ws/tmsImsNo
浏览器访问时因接口协议访问地址需加上后缀如下:
http://localhost:8080/fsscbs/ws/tmsImsNo?wsdl
其中
是定义的拦截器
webservice拦截器:
继承:AbstractPhaseInterceptor message有好多种
public class InMessageInterceptor extends AbstractPhaseInterceptor{
protected Logger log;
public InMessageInterceptor(){
this(Phase.RECEIVE, InMessageInterceptor.class.getName());
}
public InMessageInterceptor(String logName){
this(Phase.RECEIVE, logName);
}
protected InMessageInterceptor(String phase, String logName)
{
super(phase);
log = Logger.getLogger(logName);
}
protected String modifyMessage(String message)
{
return message;
}
public void handleMessage(Message message) {
try {
String serviceName="";
Exchange exchange = message.getExchange();
if(exchange!=null && exchange.getService()!=null)
serviceName = exchange.getService().getName().toString();
log.info("Service = " + serviceName);
InputStream is = message.getContent(InputStream.class);
String encode = (String) message.get(Message.ENCODING);
String xml = IOUtils.toString(is, encode);
if (log.isInfoEnabled()) {
log.info("Service = " + serviceName + ", soap message = " + xml);
}
xml = modifyMessage(xml);
message.setContent(InputStream.class, new ByteArrayInputStream(xml.getBytes(encode)));
}
catch (Exception e) {
if (log.isEnabledFor(Level.DEBUG))
log.error("Error when split original inputStream. CausedBy : " + "\n" + e);
}
}
}
增加安全校验使用拦截器:
public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
public AuthInterceptor() {
super(Phase.PRE_INVOKE); //拦截器在调用方法之前拦截SOAP消息
}
// 拦截器操作
@Override
public void handleMessage(SoapMessage msg) throws Fault {
System.out.println("come to auth interceptor...");
//获取SOAP消息的所有Header
List headers = msg.getHeaders();
if(headers == null || headers.size() < 1) {
throw new Fault(new IllegalArgumentException("没有Header,拦截器实施拦截"));
}
//获取Header携带是用户和密码信息
Header firstHeader = headers.get(0);
Element element = (Element) firstHeader.getObject();
NodeList userNameElement = element.getElementsByTagName("userName");
NodeList passwordElement = element.getElementsByTagName("password");
if (userNameElement.getLength() != 1) {
throw new Fault(new IllegalArgumentException("用户名格式不对"));
}
if (passwordElement.getLength() != 1) {
throw new Fault(new IllegalArgumentException("用户密码格式不对"));
}
//获取元素的文本内容
String userName = userNameElement.item(0).getTextContent();
String password = passwordElement.item(0).getTextContent();
// 实际项目中, 应该去查询数据库, 该用户名,密码是否被授权调用该webservice
if (!userName.equals("zheng") || !password.equals("123456")) {
throw new Fault(new IllegalArgumentException("用户名或密码不正确"));
}
}
}
客户端调用webservice接口相关知识如下:
使用jdk或者saopui生成客户端代码。
调用接口客户端代码如下:
JaxWsProxyFactoryBean svr = new JaxWsProxyFactoryBean();
svr.setServiceClass(TmsImsWebService.class);
svr.setAddress("http://localhost:8080/fsscbs/ws/tmsImsNo");
TmsImsWebService hw = (TmsImsWebService) svr.create();
System.out.println(hw.mtTMSECCFITMSYXNO("###", "####", "###"));
备注:上述代码可考虑写静态方法作为工具类使用,调用接口时调用工具类即可
接口调用:BISSoapEntryBISInvoke 为接口名称:
BISSoapEntryBISInvoke tmsCancelPort = ProxyFactory.getTmsCancelPort(BISSoapEntryBISInvoke.class);
ProxyFactory 工具类如下
public class ProxyFactory {
public static T getMobile(Class c) {
String address = getAddress("Mobile_Url");
// OutMessageInterceptor i = new OutMessageInterceptor();
return getPort(c, address, null);
}
public static String getAddress(String propertyName) {
PropertiesUtil util = new PropertiesUtil("cxf.properties");
String address = util.getValue(propertyName);
return address;
}
@SuppressWarnings("unused")
private static String getAddress(String path, String propertyName) {
PropertiesUtil util = new PropertiesUtil(path);
String address = util.getValue(propertyName);
return address;
}
// 获取WebService接口
public static T getPort(Class c, String address) {
return (T) getPort(c, address, null);
}
@SuppressWarnings("unchecked")
private static T getPort(Class c, String address, Interceptor> interceptor) {
JaxWsProxyFactoryBean soapFactoryBean = new JaxWsProxyFactoryBean();
soapFactoryBean.setAddress(address);
soapFactoryBean.setServiceClass(c);
if (interceptor != null) {
if (interceptor instanceof OutMessageInterceptor) {
soapFactoryBean.getOutInterceptors().add(interceptor);
}
if (interceptor instanceof InMessageInterceptor) {
soapFactoryBean.getInInterceptors().add(interceptor);
}
}
return (T) soapFactoryBean.create();
}
}
PropertiesUtil 工具类操作properties文件:
package com.guo.hui.util;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
public class PropertiesUtil {
private String fileName;
public PropertiesUtil(String fileName) {
this.fileName = fileName;
}
/**
* 获得数据库配置信息
*
* @return
* @throws IOException
*/
private Properties loadProperties() throws IOException {
Properties p = null;
InputStream inputStream = null;
try {
p = new Properties();
Resource resource = new ClassPathResource(this.fileName);
inputStream = resource.getInputStream();
p.load(inputStream);
} catch (IOException e) {
if (inputStream != null) {
inputStream.close();
}
throw e;
}
return p;
}
private Properties properties;
// 获取值
public String getValue(String key) {
if (properties == null) {
try {
properties = this.loadProperties();
} catch (IOException e) {
System.out.print(e.getMessage());
}
}
if (properties != null) {
return properties.getProperty(key);
}
return null;
}
public Set> getData() {
if (properties == null) {
try {
properties = this.loadProperties();
} catch (IOException e) {
System.out.print(e.getMessage());
}
}
if (properties != null) {
return properties.entrySet();
// return properties.getProperty(key);
}
return null;
}
}
备注:String address = getAddress(“TMS_Status_ServiceUrl”);
TMS_Status_ServiceUrl是读取配置文件(properties)地址:
TMS_Status_ServiceUrl={webserviceroot}/webservices/##{webserviceroot} el表达式去maven项目pom文件中的地址,是接口能适应不同运行环境(生产,预生产,测试等)
pom中的配置:IP+端口+通用地址部分
http://IP:port/#
客户端可设置拦截器,记录日志或者增加用户密码,示例代码如下:
import javax.xml.namespace.QName;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.hpzc.model.exception.BizException; //自定义异常类
public class AddHeaderInterceptor extends AbstractPhaseInterceptor {
private String userName;
private String password;
public AddHeaderInterceptor(String userName, String password) {
super(Phase.PREPARE_SEND);
this.userName = userName;
this.password = password;
}
@Override
public void handleMessage(SoapMessage msg) throws BizException {
List headers = msg.getHeaders();
// 创建Document对象
Document document = DOMUtils.createDocument();
Element element = document.createElement("authHeader");
// 配置服务器端Head信息的用户密码
Element userNameElement = document.createElement("userName");
userNameElement.setTextContent(userName);
System.out.println(userNameElement.getElementsByTagName("userName"));
Element passwordElement = document.createElement("password");
passwordElement.setTextContent(password);
element.appendChild(userNameElement);
element.appendChild(passwordElement);
headers.add(new Header(new QName(""), element));
/**
* 生成的XML文档 zheng
* 1111
*/
}
}
备注:userNameElement.setTextContent(userName);
passwordElement.setTextContent(password); 可能会报错.报错可能原因是xml-api.jar包冲突.
org.w3c.dom.Element 缺少 setTextContent 方法
org.w3c.dom.Element没有setTextContent方法 的编译错。
主要是 xercesImpl.jar 和 xml-apis.jar的Element版本太老造成的,setTextContent是DOM3中的方法。
但在pom文件中,未发现有直接引用这两个jar包的地方。好在eclipse maven插件功能强大,在Project Explorer的Maven Dependencies中
找到了xml-api.jar,然后右键->Maven->Exclude Maven artifact,弹出窗口,提示会在当前pom文件中的hibernate-core依赖中,排除掉
xml-api.jar,确认后,pom文件被修改,更新Maven Dependencies,重新编译,问题解决
假设访问接口时增加了http校验也就是访问接口时先验证则可以增加如下配置,通过验证。
cxf配置文件中增加如下配置,可以增加用户/密码
配置文件增加验证:pom中的配置:IP+端口+通用地址部分相同
ims
ims
http:tlsClientParameters
._EXPORT_.
._EXPORT1024_.
._WITH_DES_.
._WITH_AES_.
._WITH_NULL_.
._DH_anon_.