文章目录
- SOAP和WSDL
- SOAP协议简介
- WebService 和 HTTP的区别
- SOAP元素介绍
- SOAP请求格式
- SOAP协议的版本和请求格式的异同
- SOAP协议的演示
- WSDL 解读
- 根据WSDL测试接口
- 示例1:
- 示例2:
- 抓包:
- 用SOCKET Send发送SOAP信息
- SOAP的安全机制
- SOAP的缺点
- SOAP XML的解释
- 拓展
- 参考
SOAP和WSDL
SOAP协议简介
WebService的三要素是SOAP、WSDL、UDDI。
其中SOAP是传输协议、WSDL是对接口的描述。
SOAP,Simple Object Access Protocol,简单对象管理协议。
是在网络编程中的一个应用层协议,它是基于HTTP和XML的,
即规定了HTTP协议的传输内容是XML格式的。
SOAP与HTTP最大的不同是,SOAP传输的是XML格式的内容。
SOAP 协议使用 XML 格式来定义消息的结构和内容。在使用 SOAP 进行通信时,发送方会将消息打包成 XML 格式,然后通过 HTTP 或 SMTP 等协议发送给接收方。接收方会解析 XML 格式的消息,从中提取出所需的信息。
SOAP 协议提供了一组规范来定义消息的结构和内容。这些规范包括 SOAP Envelope、SOAP Header 和 SOAP Body。SOAP Envelope 是 SOAP 消息的根元素,它包含了整个 SOAP 消息的描述信息。SOAP Header 是可选的,用于传递与消息相关的其他信息,如安全认证信息。SOAP Body 包含了实际的消息内容。
来源:https://apifox.com/apiskills/soap/
WebService 和 HTTP的区别
HTTP(超文本传输协议)是一种用于在Web上传输数据的协议。它是Web服务中最常用的协议之一,用于在客户端和服务器之间传输请求和响应。HTTP是基于请求-响应模型的,客户端发送HTTP请求到服务器,服务器处理请求并返回HTTP响应。HTTP协议使用URL(统一资源定位符)来标识资源,并使用不同的HTTP方法(如GET、POST、PUT、DELETE)来执行不同的操作。
下面是Web服务和HTTP之间的一些区别:
- Web服务是一种软件系统的概念,而HTTP是一种用于传输数据的协议。Web服务使用HTTP作为通信的基础协议之一,但并不限于HTTP,还可以使用其他协议如SOAP(Simple Object Access Protocol)和REST(Representational State Transfer)。
- Web服务提供了一种统一的方式来描述和公开应用程序的功能和接口,使得不同平台和编程语言的应用程序能够互操作。HTTP只是Web服务中的一部分,用于在客户端和服务器之间传输数据。
- Web服务通常使用XML作为数据交换的格式,以便于数据的解析和处理。HTTP协议可以传输多种类型的数据,包括HTML、XML、JSON等。
- Web服务通常使用WSDL(Web Services Description Language)来描述服务的接口和功能。WSDL是一种基于XML的语言,用于描述Web服务的操作、消息和数据类型。HTTP没有提供类似的描述功能。
总之,Web服务是一种用于实现不同应用程序之间通信和交互的软件系统概念,而HTTP是一种用于在Web上传输数据的协议,它是Web服务中最常用的通信协议之一。
来源:https://www.juming.com/zx/21359.html
SOAP元素介绍
简单的SOAP元素介绍:
runoob.com SOAP 教程
SOAP请求格式
一个SOAP请求,就是一个HTTP请求,重点是HTTP Body部分是XML格式的。
SOAP XML包含两个最基本的元素:SOAP Envelope 和 SOAP Body。
SOAP Envelope 是必须的元素,是SOAP消息的根元素,其中规定了这条SOAP请求的命名空间。
SOAP Body中规定了,这条SOAP请求的内容。
例如,getRegionCountry这个接口的SOAP 1.1请求:
POST /WebServices/WeatherWS.asmx HTTP/1.1
Host: ws.webxml.com.cn
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://WebXml.com.cn/getRegionCountry"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<getRegionCountry xmlns="http://WebXml.com.cn/" />
</soap:Body>
</soap:Envelope>
HTTP Header
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://WebXml.com.cn/getRegionCountry"
是SOAP 1.1 必须的属性(这里和SOAP 1.2有区别)。
HTTP Body - SOAP Envelope
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
首先这个soap:Envelope中,xmlns是 xml namespace的意思,
xmlns:xsi这里表示引入了"http://www.w3.org/2001/XMLSchema-instance" 命名空间,
xmlns:xsd这里表示引入了"http://www.w3.org/2001/XMLSchema"命名空间,
xmlns:soap这里表示soap:Envelope的命名空间是"http://schemas.xmlsoap.org/soap/envelope/"。
** SOAP 1.1 :Envelope的命名空间规定是http://schemas.xmlsoap.org/soap/envelope/ **
** SOAP 1.2 :Envelope的命名空间规定是http://www.w3.org/2003/05/soap-envelope/**
其次,这个SOAP Envelope 引入了xsi、xsd 命名空间,可以使用其中定义的元素。
HTTP Body - SOAP Body
<soap:Body>
<getRegionCountry xmlns="http://WebXml.com.cn/" />
</soap:Body>
这个SOAP body请求在"http://WebXml.com.cn/"命名空间中的getRegionCountry 接口的返回值。
SOAP协议的版本和请求格式的异同
SOAP协议 有1.1 和 1.2 两个版本。这两个版本是都在使用的。
使用时区别如下,在下面部分会演示他们使用时候的不同:
-
http head 的Content-Type
v1.1: text/xml
v1.2: application/soap+xml -
http head的SOAPAction
v1.1:需要这个字段指向需要操作的API
v1.2:没有这个字段 -
http body xml部分的命名空间
v1.1和v1.2 的 xml envelope中xml namespace不同
SOAP 1.1 :Envelope的命名空间规定是http://schemas.xmlsoap.org/soap/envelope/
SOAP 1.2 :Envelope的命名空间规定是http://www.w3.org/2003/05/soap-envelope/
SOAP协议的演示
服务端:WeatherWS,WeatherWS WSDL
客户端:使用Postman进行接口测试
进行SOAP通信,需要服务端提供接口信息。
在WeatherWS中对每个接口都进行了详细的HTTP或者SOAP消息描述,用Postman完全可以参照它进行接口测试。
但是我想从WSDL开始解析,分析一下为什么一个接口是这样的SOAP消息。
WSDL 解读
WeatherWS WSDL,是WeatherWS的WSDL信息。
WSDL是Web Services Description Language,它是用来描述WebService的接口的。
他主要有以下几种元素:
wsdl:definition wsdl的根元素
wsdl:types 使用的数据类型
wsdl:message 消息,它负责包装输入参数和响应结果、异常信息等
wsdl:portType 指定端口的类型
wsdl:operation 某项服务能完成的动作
wsdl:documentation 文本
wsdl:input 输入变量
wsdl:output 输出变量
wsdl:binding wsdl使用的通信协议
wsdl:port 一个端口
wsdl:service 由端口组成的服务口
以WeatherWS WSDL中的部分内容举例:
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://WebXml.com.cn/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://WebXml.com.cn/">
↑WSDL的根元素,这里引入了命名空间,targetNamespace就是当前文件的命名空间。
<wsdl:service name="WeatherWS">
~
<wsdl:port name="WeatherWSSoap" binding="tns:WeatherWSSoap">
<soap:address location="http://ws.webxml.com.cn/WebServices/WeatherWS.asmx"/>
</wsdl:port>
</wsdl:service>
↑这里定义了一个WeatherWS Service。
服务中包含一个WeatherWSSoap Port,看名字就知道,它负责的是SAOP 1.1 通信,它绑定了tns命名空间中的 <wsdl:binding name=“WeatherWSSoap” type=“tns:WeatherWSSoap”>。
soap:address location就是WebService的地址URL。
<wsdl:binding name="WeatherWSSoap" type="tns:WeatherWSSoap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
~
<wsdl:operation name="getWeather">
<soap:operation soapAction="http://WebXml.com.cn/getWeather" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
↑这里定义了一个binding WeatherWSSoap, 它的type是 <wsdl:portType name=“WeatherWSSoap”>。
soap:binding transport 属性定义了要使用的协议。
soap:operation soapAction 定义了soapAction,这里是Postman的http header之一。
这里还定义了getWeather Operation的输入输出编码。
<soap:body use=“literal” />,use用于确定SOAP消息的编码方式,
WSDL的binding标签中 use/style 属性详解
<wsdl:portType name="WeatherWSSoap">
~
<wsdl:operation name="getWeather">
<wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"><br /><h3>获得天气预报数据</h3><p>输入参数:城市/地区ID或名称,返回数据:一维字符串数组。</p><br /></wsdl:documentation>
<wsdl:input message="tns:getWeatherSoapIn" />
<wsdl:output message="tns:getWeatherSoapOut" />
</wsdl:operation>
</wsdl:portType>
↑这里定义了一个WeatherWSSoap portType。
WeatherWSSoap portType中,定义了operation的message信息。
例如:getWeather operation,有一个输入对象message getWeatherSoapIn,有一个输出对象 message getWeatherSoapOut。
<wsdl:message name="getWeatherSoapIn">
<wsdl:part name="parameters" element="tns:getWeather" />
</wsdl:message>
<wsdl:message name="getWeatherSoapOut">
<wsdl:part name="parameters" element="tns:getWeatherResponse" />
</wsdl:message>
↑继续追踪,找到上面的两个message。可以在WSDL中找到如下element:
<s:element name="getWeather">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="theCityCode" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="theUserID" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="getWeatherResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="getWeatherResult" type="tns:ArrayOfString" />
</s:sequence>
</s:complexType>
</s:element>
complexType是一个复杂类型,minOccurs表示出现的最小次数,maxOccurs表示出现的最大次数。
根据WSDL测试接口
上面我们非常简略的梳理了WeatherWS WSDL的结构。
现在根据我们从WSDL中了解的接口信息,进性Postman接口测试。
示例1:
根据上面的梳理,我们知道,WSDL中:
service "WeatherWS”定义了port “WeatherWSSoap”; //它是SOAP 1.1 端口
port "WeatherWSSoap"绑定了binding “WeatherWSSoap”;
binding "WeatherWSSoap"中定义了operation “getWeather”;
operation "getWeather"中有一个input和一个output。
operation "getWeather"的数据类型是portType “WeatherWSSoap”;
portType "WeatherWSSoap"中operation "getWeather"的input message和output message分别是:message “getWeatherSoapIn"和message getWeatherSoapOut”。
根据message 可以找到element,知道输入element "getWeather"包含2个参数:theCityCode和theUserID,element "getWeatherResponse"包含1个返回值getWeatherResult。
在port "WeatherWSSoap"标签中,可以知道,SOAP请求的URL是:
location=“http://ws.webxml.com.cn/WebServices/WeatherWS.asmx”
在operation "getWeather"中,可以知道,SOAPAction是:
soapAction=“http://WebXml.com.cn/getWeather”
可以配置Postman HTTP Header:
配置Postman HTTP Header:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<getWeather xmlns="http://WebXml.com.cn/">
<theCityCode>3643</theCityCode>
<theUserID/>
</getWeather>
</soap:Body>
</soap:Envelope>
3643,这里的CityCode是通过getSupportCityString接口,提前获取的上海徐家汇的ID。
返回值:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<getWeatherResponse xmlns="http://WebXml.com.cn/">
<getWeatherResult>
<string>直辖市 上海</string>
<string>徐家汇</string>
<string>3643</string>
<string>2024/02/07 10:37:00</string>
<string>今日天气实况:气温:4.6℃;风向/风力:北风 0级;湿度:69%</string>
<string>紫外线强度:最弱。</string>
<string>感冒指数:易发,天冷易感冒,注意防范。
运动指数:较不宜,天气寒冷,推荐您进行室内运动。
过敏指数:极不易发,无需担心过敏,可放心外出,享受生活。
穿衣指数:冷,建议着棉衣加羊毛衫等冬季服装。
洗车指数:适宜,天气较好,适合擦洗汽车。
紫外线指数:最弱,辐射弱,涂擦SPF8-12防晒护肤品。
</string>
<string>2月7日 阴转晴</string>
<string>1℃/5℃</string>
<string>无持续风向小于3级</string>
<string>2.gif</string>
<string>0.gif</string>
<string>2月8日 多云转阴</string>
<string>0℃/7℃</string>
<string>无持续风向小于3级</string>
<string>1.gif</string>
<string>2.gif</string>
<string>2月9日 多云转晴</string>
<string>2℃/9℃</string>
<string>无持续风向小于3级</string>
<string>1.gif</string>
<string>0.gif</string>
<string>2月10日 晴</string>
<string>4℃/13℃</string>
<string>无持续风向小于3级</string>
<string>0.gif</string>
<string>0.gif</string>
<string>2月11日 晴</string>
<string>4℃/12℃</string>
<string>无持续风向小于3级</string>
<string>0.gif</string>
<string>0.gif</string>
</getWeatherResult>
</getWeatherResponse>
</soap:Body>
</soap:Envelope>
示例2:
这次使用SOAP 1.2 的port "WeatherWSSoap12"的接口来测试。
WeatherWSSoap12的getWeather接口,和WeatherWSSoap的getWeather接口,参数配置一样,只是SOAP协议版本不一样。
注意:↑xmlns:soap12 用的是SOAP1.2的命名空间。
返回值:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<getWeatherResponse xmlns="http://WebXml.com.cn/">
<getWeatherResult>
<string>直辖市 上海</string>
<string>徐家汇</string>
<string>3643</string>
<string>2024/02/07 10:37:00</string>
<string>今日天气实况:气温:4.6℃;风向/风力:北风 0级;湿度:69%</string>
<string>紫外线强度:最弱。</string>
<string>感冒指数:易发,天冷易感冒,注意防范。
运动指数:较不宜,天气寒冷,推荐您进行室内运动。
过敏指数:极不易发,无需担心过敏,可放心外出,享受生活。
穿衣指数:冷,建议着棉衣加羊毛衫等冬季服装。
洗车指数:适宜,天气较好,适合擦洗汽车。
紫外线指数:最弱,辐射弱,涂擦SPF8-12防晒护肤品。
</string>
<string>2月7日 阴转晴</string>
<string>1℃/5℃</string>
<string>无持续风向小于3级</string>
<string>2.gif</string>
<string>0.gif</string>
<string>2月8日 多云转阴</string>
<string>0℃/7℃</string>
<string>无持续风向小于3级</string>
<string>1.gif</string>
<string>2.gif</string>
<string>2月9日 多云转晴</string>
<string>2℃/9℃</string>
<string>无持续风向小于3级</string>
<string>1.gif</string>
<string>0.gif</string>
<string>2月10日 晴</string>
<string>4℃/13℃</string>
<string>无持续风向小于3级</string>
<string>0.gif</string>
<string>0.gif</string>
<string>2月11日 晴</string>
<string>4℃/12℃</string>
<string>无持续风向小于3级</string>
<string>0.gif</string>
<string>0.gif</string>
</getWeatherResult>
</getWeatherResponse>
</soap:Body>
</soap:Envelope>
抓包:
发送SOAP请求:
接收返回结果:
用SOCKET Send发送SOAP信息
我们知道SOAP是基于HTTP的应用层协议。
那么我们用一个SOCKET,直接TCP Send配置好的SOAP请求是不是可以?
可以的,代码如下:
编译环境:VS 2022
头文件:
#include<WinSock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <tchar.h>
//#import <msxml6.dll>
#include "tinystr.h" //tinyxml
#include "tinyxml.h" //tinyxml
#pragma comment(lib, "ws2_32.lib")
本来想用MSXML,但是太麻烦了,按照MS示例,添加XML声明,竟然添加的都不完全正确,搞了半天没找到什么详细文档。
索性用tinyxml库了,用了以后so easy。
以下是完整的main函数,分开几段来记录。
初始化SOCKET。
int main()
{
WSADATA data;
WSAStartup(MAKEWORD(2,2),&data);
SOCKET conn_fd = socket(AF_INET,SOCK_STREAM,0);
if (conn_fd == -1)
return -1;
ADDRINFO Hints , *pRes = nullptr, *pCur = nullptr;
memset(&Hints, 0x00, sizeof(ADDRINFO));
Hints.ai_family = AF_INET;
Hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo("ws.webxml.com.cn", "HTTP", &Hints, &pRes) == -1)
{
std::cout << GetLastError() << std::endl;
return -1;
}
pCur = pRes;
初始化HTTP Header
while (pCur != NULL)
{
int res = connect(conn_fd,pCur->ai_addr,sizeof(sockaddr));
if (res != -1)
{
char buff[4096] = { 0x00 };
char buff2[4096*2] = { 0x00 };
sprintf(buff, "%s%s%s%s%s%s%s",
"POST /WebServices/WeatherWS.asmx HTTP/1.1\r\n",
"Host: ws.webxml.com.cn\r\n",
"Content-Type: text/xml; charset=utf-8\r\n",
"User-Agent: PostmanRuntime / 7.36.0\r\n", //这里是抄的Postman的网络抓包中的UA设置,没有这个会返回, 500 internal server error
"Content-Length: 403\r\n", //在SOAP XML部分编辑好后,获取的XML长度,再写在这里
"SOAPAction: \"http://WebXml.com.cn/getWeather\"\r\n",
"\r\n"
);
创建xml对象,编辑SOAP xml内容
TiXmlDocument* tinyXmlDoc = new TiXmlDocument();
TiXmlDeclaration* tinyXmlDeclare = new TiXmlDeclaration("1.0", "utf-8", ""); // 声明头部格式
tinyXmlDoc->LinkEndChild(tinyXmlDeclare);
// 创建时需要指定根节点的名称
TiXmlElement* pEnvelope = new TiXmlElement("soap:Envelope");
pEnvelope->SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
pEnvelope->SetAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
pEnvelope->SetAttribute("xmlns:soap", "http://schemas.xmlsoap.org/soap/envelope/");
tinyXmlDoc->LinkEndChild(pEnvelope);
TiXmlElement* pBody = new TiXmlElement("soap:Body");
pEnvelope->LinkEndChild(pBody);
TiXmlElement* pGetWeather = new TiXmlElement("getWeather");
pGetWeather->SetAttribute("xmlns", "http://WebXml.com.cn/");
pBody->LinkEndChild(pGetWeather);
TiXmlElement* pTheCityCode = new TiXmlElement("theCityCode");
TiXmlText* pText = new TiXmlText("3634"); //这里的CityCode是提前在WeatherWS上用接口获取好的
pTheCityCode->LinkEndChild(pText);
pGetWeather->LinkEndChild(pTheCityCode);
TiXmlElement* pTheUserID = new TiXmlElement("theUserID");
pGetWeather->LinkEndChild(pTheUserID);
TiXmlPrinter printer;
tinyXmlDoc->Accept(&printer);
printf("%s\n", printer.CStr());
把xml内容追加到HTTP Header后面。
std::string str(printer.CStr());
int size = str.length();
strcat(buff, str.c_str());
发送包装好的HTTP SOAP消息,接收返回值。把返回的消息写入一个文件。
int count = send(conn_fd, buff, strlen(buff),0);
std::cout << ">>>Send:" << count << "Bytes\n" << buff << std::endl;
count = recv(conn_fd, buff2, 4096 * 2, 0);
std::cout << "\n\n>>>Recv:" << count << "Bytes\n" << buff2 << std::endl;
std::ofstream test_file;
test_file.open("d:\\res.xml", std::ios::out | std::ios::binary);
test_file << buff2;
test_file.close();
closesocket(conn_fd);
}
else
{
std::cout << WSAGetLastError() << std::endl;
}
pCur = pRes->ai_next;
}
WSACleanup();
return 0;
}
Send发出的消息内容:
WeatherWS返回的结果,写入文件:
稍微排了一下版,确实收到了WeatherWS的回复。通信成功。
SOAP的安全机制
TODO。
目前想的是,可以依托HTTPS来进行安全通信。
SOAP的缺点
SOAP传输的是XML格式的内容,XML格式是的传输内容更加冗长,不够轻量。
SOAP XML的解释
SOAP传输的是XML格式的内容,获取SOAP消息,需要对XML内容解释。
因为之前工作中用过MSXML,记录在了这里:
[xml] MSXML6读/写/修改xml文件的简单实现
但是更推荐Tinyxml来操作,代码简便。MSXML有效文档不好找。
拓展
REST vs SOAP: 两种 Web 服务协议的分析
C++实现一个SOAP客户端
webservie+soap+wsdl入门
postman测试webservices接口
参考
WSDL的binding标签中 use/style 属性详解
C/C++ 使用 tinyxml库 操作XML格式文件(创建、插入、删除、修改、解析)
SOAP和WSDL的一些必要知识