soap是什么
SOAP 是一种网络通信协议
SOAP即Simple Object Access Protocol简易对象访问协议
SOAP 用于跨平台应用程序之间的通信
SOAP 被设计用来通过因特网(http)进行通信
SOAP = HTTP+XML,其实就是通过HTTP发xml数据
SOAP 很简单并可扩展支持面向对象
SOAP 允许您跨越防火墙
SOAP 将被作为 W3C 标准来发展
使用TCP/IP Monitor监视Soap协议
使用TCP/IP Monitor可以监视tcp/ip协议的报文内容,由于http是基于Tcp的应用协议,而webservice是基于http实现,所以通过tcp/ip monitor可以监视webservice请求及响应的内容。
Soap1.1:
客户端代码:
//定义url,参数为wsdl地址 URL url = new URL("http://127.0.0.1:54321/weather?wsdl"); //定义qname,第一个参数是命名空间,第二个参数名称是wsdl里边的服务名 QName qName = new QName("http://server.jaxws.webservice.itcast.cn/", "WeatherInterfaceImplService"); //创建服务视图 Service service = Service.create(url, qName); //通过服务视图得到服务端点 WeatherInterfaceImpl weatherInterfaceImpl =service.getPort(WeatherInterfaceImpl.class); //调用webservice System.out.println(weatherInterfaceImpl.queryWeather("郑州"));
请求:
注意蓝底标注
POST /weather HTTP/1.1 Accept: text/xml, multipart/related Content-Type: text/xml; charset=utf-8 SOAPAction: "http://server.jaxws.ws.itcast.cn/WeatherServer/queryWeatherRequest" User-Agent: JAX-WS RI 2.2.8 svn-revision#13980 Host: 127.0.0.1:4321 Connection: keep-alive Content-Length: 232 <?xml version='1.0' encoding='UTF-8'?> <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Body> <ns2:queryWeather xmlns:ns2="http://server.jaxws.ws.itcast.cn/"> <arg0>郑州</arg0> </ns2:queryWeather> </S:Body> </S:Envelope>
响应:
HTTP/1.1 200 OK Transfer-encoding: chunked Content-type: text/xml; charset=utf-8 <?xml version='1.0' encoding='UTF-8'?> <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Body> <ns2:queryWeatherResponse xmlns:ns2="http://server.jaxws.ws.itcast.cn/"> <return>天气晴朗</return> </ns2:queryWeatherResponse> </S:Body> </S:Envelope>
soap协议体包含下列元素
必需有 Envelope 元素,此元素将整个 XML 文档标识为一条 SOAP 消息
可选的 Header 元素,包含头部信息
必需有Body 元素,包含所有的调用和响应信息
可选的 Fault 元素,提供有关在处理此消息所发生错误的信息
soap消息基本结构
<?xml version="1.0"?> <soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope" soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding"> <soap:Header> ... ... </soap:Header> <soap:Body> ... ... <soap:Fault> ... ... </soap:Fault> </soap:Body> </soap:Envelope>
http发送soap协议测试
webservice使用soap协议传输数据,soap是基于http的应用协议,可以使用http发送soap协议数据完成webservice的请求。
本例子解析响应的xml数据使用dom4j。
/** * 通过http发送soap协议请求webservice * @author SMN * @version V1.0 */ public class HttpRequestSoap { public static void main(String[] args) throws IOException { //webservice地址 String webservice_url = "http://127.0.0.1:1234/weather"; //发送的soap协议内容 String soap_xml = soap_xml("郑州"); System.out.println(soap_xml); //创建url URL url = new URL(webservice_url); //创建http链接对象 HttpURLConnection httpURLConnection = (HttpURLConnection)url.openConnection(); //设置请求方法 httpURLConnection.setRequestMethod("POST"); //设置Content-type httpURLConnection.setRequestProperty("Content-type", "text/xml;charset=\"utf-8\""); //使用http进行输出 httpURLConnection.setDoOutput(true); //使用http进行输入 httpURLConnection.setDoInput(true); //通过输出流发送数据 OutputStream outputStream = httpURLConnection.getOutputStream(); outputStream.write(soap_xml.getBytes()); outputStream.close(); //接收服务端响应数据 InputStream inputStream = httpURLConnection.getInputStream(); //使用buffer存在读取的数据 byte[] buffer = new byte[1024]; //使用字节输出流存储读取的数据 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); while(true){ int len = inputStream.read(buffer); //如果流水读取完则退出循环 if(len == -1){ break; } byteArrayOutputStream.write(buffer,0,len); } //得到响应数据 String response_string = byteArrayOutputStream.toString(); System.out.println(response_string); parseXml(response_string); } //soap协议内容 public static String soap_xml(String cityName){ String soap_xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">" + "<S:Body>" + "<ns2:queryWeather xmlns:ns2=\"http://impl.sei.jaxws.ws.itcast.cn/\">" + "<arg0>"+ cityName + "</arg0>" + "</ns2:queryWeather>" + "</S:Body>" + "</S:Envelope>"; return soap_xml; } //解析响应的xml public static String parseXml(String xmlString){ String result = null; try { Document document = DocumentHelper.parseText(xmlString); //创建xpath解析对象 DefaultXPath defaultXPath = new DefaultXPath("//ns2:queryWeatherResponse"); //指定命名空间 defaultXPath.setNamespaceURIs(Collections.singletonMap("ns2", "http:// impl.sei.jaxws.ws.itcast.cn/")); List<Element> elements= defaultXPath.selectNodes(document); Element response = elements.get(0); List<Element> results = response.selectNodes("return"); System.out.println(results.get(0).getText()); } catch (DocumentException e) { e.printStackTrace(); } return result; } }
Soap1.2:
下载 jaxws-ri-2.2.8
Jaxws实现soap1.2需要加入jaxws扩展包,从sun下载jaxws-ri-2.2.8,解压jaxws-ri-2.2.8并将lib下的jar包加载到java工程中。
添加BindingType
在SEI实现类上添加如下注解
@BindingType(javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_BINDING)
请求:
POST /weather HTTP/1.1 Accept: application/soap+xml, multipart/related Content-Type: application/soap+xml; charset=utf-8;action="http://server.jaxws.ws.itcast.cn/WeatherServer/queryWeatherRequest" User-Agent: JAX-WS RI 2.2.8 svn-revision#13980 Host: 127.0.0.1:4321 Connection: keep-alive Content-Length: 230 <?xml version='1.0' encoding='UTF-8'?> <S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope"> <S:Body> <ns2:queryWeather xmlns:ns2="http://server.jaxws.ws.itcast.cn/"> <arg0>郑州</arg0> </ns2:queryWeather> </S:Body> </S:Envelope>
响应:
HTTP/1.1 200 OK Transfer-encoding: chunked Content-type: application/soap+xml; charset=utf-8 <?xml version='1.0' encoding='UTF-8'?> <S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope"> <S:Body> <ns2:queryWeatherResponse xmlns:ns2="http://server.jaxws.ws.itcast.cn/"> <return>天气晴朗</return> </ns2:queryWeatherResponse> </S:Body> </S:Envelope>
Soap1.1与soap1.2异同
相同之处:
soap1.1和soap1.2都是使用post方法
都包括Envelope和body
内容类型context-type不同:
soap1.1使用text/xml
soap1.2使用application/soap+xml
webservice 发送xml数据
由于xml的跨平台特性,企业中在实际开发接口时方法只定义一个参数传递复杂的xml数据,这样做可以省去自定义复杂java数据类型的麻烦,且webservice接口简单,接口双方将xml数据格式规定好,实质上是通过webservice的soap协议传递xml数据。
功能说明:
创建区域查询webservice服务,客户端调用服务端查询区域信息,客户端向服务端传递xml格式数据,服务端向客户端响应xml格式数据。
接口描述:
客户端发送数据格式:
<?xml version="1.1" encoding="utf-8"?> <queryarea> <parentid> </parentid>//父级区域id <start></start>//起始记录,从1开始 <end></end>//结束记录 </queryarea>
服务端响应数据格式:
<?xml version="1.0" encoding="UTF-8"?> <areas> <area> <areaid> </areaid>//区域id <areaname></areaname>//区域名称 <arealevel></arealevel>//区域等级 <parentid></parentid>//父级区域id </area> //….. </areas>
服务端:
Dao
public class Area { private String areaid; private String areaname; private String parentid; private String arealevel; private int start; private int end; ... ...
public interface AreaDao { /** * 区域查询 * @param parentid 父级区域id * @param start 查询开始下标 * @param end 查询结束下标 * @return * @throws Exception */ public List<Area> queryArea(String parentid,int start,int end) throws Exception; }
public class AreaDaoImpl implements AreaDao { //区域查询sql private static String sql = "SELECT areaid,areaname,arealevel,parentid FROM AREA where parentid = ? LIMIT ?,?"; public List<Area> queryArea(String parentid,int start,int end){ //数据库链接 Connection connection = null; //预编译statement PreparedStatement preparedStatement = null; //结果集 ResultSet resultSet = null; //区域列表 List<Area> areaList = new ArrayList<Area>(); try { //加载数据库驱动 Class.forName("com.mysql.jdbc.Driver"); //连接数据库 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/webservice", "root", "mysql"); //创建preparedStatement preparedStatement = connection.prepareStatement(sql); //查询的记录数 int length = end - start +1; //起始坐标 start = start -1; //设置查询参数 preparedStatement.setString(1, parentid); preparedStatement.setInt(2, start); preparedStatement.setInt(3, length); //获取结果集 resultSet = preparedStatement.executeQuery(); //结果集解析 while(resultSet.next()){ Area area = new Area(); area.setAreaid(resultSet.getString("areaid")); area.setAreaname(resultSet.getString("areaname")); area.setArealevel(resultSet.getString("arealevel")); area.setParentid(resultSet.getString("arealevel")); areaList.add(area); } } catch (Exception e) { e.printStackTrace(); } return areaList; } }
service
public interface AreaService { /** * 区域查询 * @param queryinfo 查询信息,xml格式详见接口描述 * @return * @throws Exception */ public String queryArea(String queryinfo) throws Exception; }
@WebService public class AreaServiceImpl implements AreaService { //区域查询dao private AreaDao areaDao = new AreaDaoImpl(); @Override public String queryArea(String queryinfo) throws Exception { //解析查询条件 Area area_query = parseXml(queryinfo); //调用dao查询区域 List<Area> listAreas = areaDao.queryArea(area_query.getParentid(),area_query.getStart(), area_query.getEnd()); //将list数据传为xml数据 Document document = DocumentHelper.createDocument(); Element root = DocumentHelper.createElement("areas"); document.setRootElement(root); for(Area area:listAreas){ Element element_area= root.addElement("area"); element_area.addElement("areaid").addText(area.getAreaid()); element_area.addElement("areaname").addText(area.getAreaname()); element_area.addElement("arealevel").addText(area.getArealevel()); element_area.addElement("parentid").addText(area.getParentid()); } //转换后的xml数据 String responseString = document.asXML(); //返回给客户端 return responseString; } //解析查询信息 private Area parseXml(String xmlString){ Area areainfo = new Area(); try { Document document = DocumentHelper.parseText(xmlString); String start = document.selectSingleNode("/queryarea/start").getText(); String end = document.selectSingleNode("/queryarea/end").getText(); String parentid = document.selectSingleNode("/queryarea/parentid").getText(); areainfo.setStart(Integer.parseInt(start)); areainfo.setEnd(Integer.parseInt(end)); areainfo.setParentid(parentid); } catch (DocumentException e) { e.printStackTrace(); } return areainfo; } }
发布服务
public class AreaServer { public static void main(String[] args) { //发布区域查询服务 Endpoint.publish("http://127.0.0.1:12345/queryarea", new AreaServiceImpl()); } }
客户端:
public class AreaClient { public static void main(String[] args) throws MalformedURLException, Exception_Exception { //区域查询服务地址 URL url = new URL("http://127.0.0.1:12345/queryarea"); QName qName =new QName("http://service.area.ws.itcast.cn/", "AreaServiceImplService"); //创建service Service service = Service.create(url, qName); //创建porttype AreaServiceImpl areaService = service.getPort(AreaServiceImpl.class); //调用服务接口查询区域 String queryString = areaService.queryArea(queryXmlString("1.",1,20)); //服务端响应的xml数据 System.out.println(queryString); //xml数据解析 parseXml(queryString); } //查询的xml信息 public static String queryXmlString(String parentid,int start,int end){ String queryString= "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<queryarea>" + "<parentid>"+parentid+"</parentid>" + "<start>"+start+"</start>" + "<end>"+end+"</end>" + "</queryarea>"; return queryString; } //将服务端响应的xml数据解析为list public static List<Area> parseXml(String xmlString){ List<Area> areas = new ArrayList<Area>(); try { Document document = DocumentHelper.parseText(xmlString); List<Node> areaList = document.selectNodes("//areas/area"); for(Node node:areaList){ Area area_i =new Area(); Element element = (Element)node; area_i.setAreaid(element.elementText("areaid")); area_i.setAreaname(element.elementText("areaname")); area_i.setArealevel(element.elementText("arealevel")); area_i.setParentid(element.elementText("parentid")); System.out.println(area_i); areas.add(area_i); } } catch (DocumentException e) { e.printStackTrace(); } return areas; } }
总结:
Webservice发送xml数据其实是将xml数据作为大字符串发送,工作量主要在解析xml数据上。虽然解析xml数据比较麻烦但是webservice接口简单,大家遵守xml格式开发接口,这种方式在企业中也较常用。
建议:数据量大的xml建议使用SAX解析提高解析速度。