soap

Java中SOAP通信详解
本文详细介绍了如何在Java中利用SOAP进行Web服务通信,包括关键类如SOAPConnection、SOAPMessage的使用方法,以及如何构建和发送SOAP请求。

接下来是关于在java中,SOAP的一些相关类和概念。

看之前需要理解SOAP的基本概念。


我们可以想到,在java 程序中发送一个SOAP request,不管是用什么方法,最终发送出去的一定就是一个标准的SOAP request。

只不过java包含一些类,代表了实际的SOAP request中不同部分。以及一些方法,帮助你构造SOAP request中的内容。


首先我们来看看,在java中是如何发送SOAP message的。

  • 第一个类: SOAPConnection

final SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
final SOAPConnection soapConnection = soapConnectionFactory.createConnection();

SOAPMessage reply = soapConnection.call(loginSOAPRequest, new URL("http://172.28.85.6/ASAPService/ASAPService_V1.svc"));

因此发送SOAP request很简单,只需要构造一个SOAPConnection对象,然后运行call方法。

其中,被发送的loginSOAPRequest是之前构造出来的一个SOAPMessage对象,发送的目的URL就是web service的地址。

由代码可见,web services返回的response也是一个SOAPMessage对象。因此我们可以认定:

在java中,发送和接受的对象都是SOAPMessage。

我们可以进一步认定: SOAPMessage这个对象一定包含了SOAP以及http头的信息。只有这样发送出去的才是一个完整有效的SOAP request。


  • 第二个类:SOAPMessage

首先明确一个概念:虽然SOAPMessage这个对象包含了http头的信息。但是java中并没有单独的类对应http头信息。

因为,http header的信息不需要代码自己构造,而是由java自动完成的。也就是说,在上文中看到的必须的http信息 POST, content_type等信息,都是自动填充的。

我们可以修改这些信息,后面将提到,但是不需要自己构造。


SOAPMessage对象包含两个部分:SOAPPart 和 AttachmentPart.  其中SOAPPart 是必须有的部分,AttachmentPart是可选的部分。

SOAPPart 其实就是完整的SOAP request。它包含http头和SOAP message信息。SOAPPart必须是XML结构的。

AttachmentPart可以有零个或多个,可以理解为,它是SOAP中可以携带的附加信息,例如图片,音频等信息。


SOAPPart  和 AttachmentPart都由下面两个部分组成:

application-specific content and associated MIME headers.


MIME ( Multipurpose Internet Mail Extension) 的概念,在javax.xml.soap有对应的类。只要是这个类的子类就可以使用SOAP传输。

其中MIME headers代表的就是下面的这部分http头信息

POST /item HTTP/1.1
Host: 189.123.345.239
Content-Type: text/plain
Content-Length: 200

SOAPAction: "http://ASAP.services.tfn.**.com/2010-03-01/Login"

但正如前面所说,我们并不需要直接构造这么一个string。SOAPMessage在实例化时会自动构造出默认的MIMEHeader,我们可以修改它:

MimeHeaders hd = loginSOAPRequest.getMimeHeaders(); //loginSOAPRequest是之前构造好的一个SOAPMessage。
hd.setHeader("SOAPAction", "http://ASAP.services.tfn.**.com/2010-03-01/Login";);

//这句将MIMEHeader中包含的SOAPAction部分由默认的“”,修改为指定的样子。

The one new piece here is that SOAP 1.1 requires the client to set a SOAPAction field in the HTTP header.

AttachmentPart可以是非XML结构的。关于AttachmentPart,本文不准备多说。


SOAPMessage类中有很多方法可以用于操作这些包含的对象。

可以使用MessageFactory来创建一个SOAPMessage。然后再向这个message中添加需要的信息。

SOAPMessage可以用来:

  • create a point-to-point connection to a specified endpoint
  • create a SOAP message
  • create an XML fragment
  • add content to the header of a SOAP message
  • add content to the body of a SOAP message
  • create attachment parts and add content to them
  • access/add/modify parts of a SOAP message
  • create/add/modify SOAP fault information
  • extract content from a SOAP message
  • send a SOAP request-response message

  • SOAPPart

看过了SOAPMessage,我们知道SOAPPart其实是SOAP消息的主体。

SOAPMessage包含SOAPPart 对象,SOAPPart 包含了SOAPEnvelope 对象,而SOAPEnvelope 又包含了SOAPBody 和SOAPHeader 对象。如下:

     SOAPPart sp = message.getSOAPPart();
     SOAPEnvelope se = sp.getEnvelope();
     SOAPBody sb = se.getBody();

     SOAPHeader sh = se.getHeader();

正如java API中描述的:SOAPPart object, which contains information used for message routing and identification, and which can contain application-specific content.

上面这些类的层次关系和SOAP协议中的语法结构是一致的。


我们可以方便的创建一个SOAPMessage。此时会有一个默认的SOAPPart属于这个SOAPMessage。

之后我们可以用SOAPPart的方法方便的往SOAPPart中添加信息:

setContent(); //Sets the content of the SOAPEnvelope object with the data from the givenSource object.

联想一下前面各个对象的关系我们就可以知道,这个方法实际set了SOAPEnvelop, SOAPBody. SOAPFault, SOAPHeader。

  • SOAPElement

An object representing an element of a SOAP message that is allowed but not specifically prescribed by a SOAP specification. This interface serves as the base interface for those objects that are specifically prescribed by a SOAP specification.

即element可以代表在SOAPMessag中任何一个element,不管这个element是SOAPBody还是SOAPEnvelope。

  • SOAPFactory

SOAPFactory is a factory for creating various objects that exist in the SOAP XML tree.SOAPFactory can be used to create XML fragments that will eventually end up in the SOAP part.

因此SOAPFactory可以用来读取XML tree,并创建对应的SOAPElement。之后可以方便的将SOAPElement加到SOAPMessage中去。

  • Transformer

它是javax.xml.soap中另外一个重要的类。在实际应用中,我们会经常使用这个类来将某个XML对象转换成DOM对象。

SOAPElement parent = SOAPFactory.newInstance().createElement("dummy");

TransformerFactory factory = TransformerFactory.newInstance();

Transformer transformer = factory.newTransformer();

transformer.transform(new DOMSource(doc), new DOMResult(parent));//将XML对象doc转换成DOM对象

return (SOAPElement) parent.getChildElements().next();//取得所有的childElement并返回

通过使用Transformer对象和SOAPElement对象,我们就可以方便的实现读入一个XML,转换成SOAPElement,之后构造成SOAPMessage,最终发送给web service。

也可以将DOM对象转换成XML对象,然后打印输出。


下面是一个极其简单的使用SOAP的示例,从文件读入一个SOAP message并发送给web service

    //Send SOAP request messages and then return response.
    public SOAPMessage sendSOAPMessage() throws Exception {
         

        //用工厂创建一个SOAPMessage
        MessageFactory mf = MessageFactory.newInstance()  ;
        SOAPMessage loginSOAPRequest = mf.createMessage();

        //给这个SOAPMessge添加信息,来源就是d:\\testSOAP.txt文件,内容是标准的SOAP
        SOAPPart sp = loginSOAPRequest.getSOAPPart();
        StreamSource prepMsg = new StreamSource(
                    new FileInputStream("d:\\testSOAP.txt"));
                    sp.setContent(prepMsg);
          
        final String SOAPACTION = "http://ASAP.services.tfn.thomson.com/2010-03-01/Login";
        //将SOAPAction添加到MimeHeaders中去
        MimeHeaders hd = loginSOAPRequest.getMimeHeaders();
        hd.setHeader("SOAPAction", SOAPACTION);

        //将上面的更改保存
        loginSOAPRequest.saveChanges();
        //创建一个SOAPConnection。具体方法在前面的SOAPConnection中已经说了         
        SOAPConnection con = getSoapConnection();
        //发送SOAP request 。这里目标地址可以是web services或tornado。
        SOAPMessage reply = con.call(loginSOAPRequest, new URL("http://172.28.85.6/ASAPService/ASAPService_V1.svc"));
        con.close();
        //返回response
        return reply;        
    }

下面这个方法接收一个SOAPMessage,并将其内容打印到文件中。据此我们可以检查构造的SOAPMessage是否是自己希望的内容。

private void getSoapContent(SOAPMessage loginSOAPRequest) {
          String output = "d:\\test1.txt";
          try{
          FileOutputStream tt = new FileOutputStream(output);
          loginSOAPRequest.writeTo(tt);
          tt.toString();
          }catch(Exception e){
              System.out.println();
          }
          
      }

/****************************************************************************** * Copyright (c) 2018-2018 TP-Link Systems Inc. * * Filename: soap.c * Version: 1.0 * Description: soap 处理函数 * Author: liyijie<liyijie@tp-link.com.cn> * Date: 2018-12-03 ******************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <arpa/inet.h> #include "onvif_passthrough.h" #include "soap_global.h" #include "soap_parse.h" #include "soap_pack.h" #define SOAP_TAG_NUM 256 typedef struct _SOAP_TAG_HANDLE { char tag[LEN_TAG]; S32 (*handle)(SOAP_CONTEXT *soap); }SOAP_TAG_HANDLE; LOCAL SOAP_TAG_HANDLE g_soap_tag_handle[SOAP_TAG_NUM]; /****************************************************************************** * 函数名称: new_soap() * 函数描述: 申请soap结构体 * 输 入: N/A * 输 出: N/A * 返 回 值: soap结构体地址 ******************************************************************************/ SOAP_CONTEXT *new_soap() { SOAP_CONTEXT *soap = NULL; soap = (SOAP_CONTEXT *)ONVIF_MALLOC(sizeof(SOAP_CONTEXT)); if (soap == NULL) { ONVIF_ERROR("malloc g_soap_for_discv failed"); return NULL; } memset(soap, 0, sizeof(SOAP_CONTEXT)); if (OK != onvif_create_buf(&soap->xml_buf, ONVIF_DISCV_BUF_LEN)) { ONVIF_FREE(soap); return NULL; } return soap; } /****************************************************************************** * 函数名称: free_soap() * 函数描述: 释放soap结构体 * 输 入: soap -- soap结构体地址 * 输 出: N/A * 返 回 值: N/A ******************************************************************************/ void free_soap(SOAP_CONTEXT *soap) { if (soap == NULL) { return; } if (soap->fault != NULL) { ONVIF_FREE(soap->fault->soap_env_value); ONVIF_FREE(soap->fault->soap_env_subcode_1); ONVIF_FREE(soap->fault->soap_env_subcode_2); ONVIF_FREE(soap->fault->soap_env_reason); ONVIF_FREE(soap->fault); } onvif_free_buf(&soap->xml_buf); ONVIF_FREE(soap); return; } /****************************************************************************** * 函数名称: soap_init() * 函数描述: 初始化soap结构体 * 输 入: soap -- soap结构体地址 * 输 出: N/A * 返 回 值: N/A ******************************************************************************/ void soap_init(SOAP_CONTEXT *soap) { if (soap == NULL) { ONVIF_WARN("soap == NULL."); return; } soap->error = OK; soap->in_ip = 0; soap->ifindex = -1; soap->has_header = FALSE; memset(&soap->header, 0, sizeof(SOAP_ENV_HEADER)); if (soap->fault != NULL) { ONVIF_FREE(soap->fault->soap_env_value); ONVIF_FREE(soap->fault->soap_env_subcode_1); ONVIF_FREE(soap->fault->soap_env_subcode_2); ONVIF_FREE(soap->fault->soap_env_reason); ONVIF_FREE(soap->fault); soap->fault = NULL; } soap->tag[0] = '\0'; soap->request_begin = NULL; soap->request_end = NULL; soap->types[0] = '\0'; soap->scopes_item[0] = '\0'; soap->scopes_matchby[0] = '\0'; if (soap->xml_buf.start == NULL || (soap->xml_buf.end - soap->xml_buf.start) > ONVIF_DISCV_BUF_LEN) { if (OK != onvif_create_buf(&soap->xml_buf, ONVIF_DISCV_BUF_LEN)) { soap->xml_buf.start = NULL; soap->xml_buf.last = NULL; soap->xml_buf.end = NULL; } } soap->xml_buf.last = soap->xml_buf.start; return; } /****************************************************************************** * 函数名称: soap_out_env_fault() * 函数描述: 组装错误信息报文内容 * 输 入: xml_buf -- 存放输出xml字串的内存地址 * fault_data -- 错误信息 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_out_env_fault(ONVIF_BUF *xml_buf, SOAP_ENV_FAULT *fault_data) { char attr[LEN_ANY] = {0}; if (xml_buf == NULL) { ONVIF_WARN("xml_buf == NULL."); return ERROR; } /* SOAP-ENV:Fault begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, G_SOAP_ENV_FAULT_STR, NULL)); /* SOAP-ENV:Code begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "SOAP-ENV:Code", NULL)); /* SOAP-ENV:Value */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "SOAP-ENV:Value", fault_data->soap_env_value, NULL)); if (fault_data && fault_data->soap_env_subcode_1) { /* SOAP-ENV:Subcode begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "SOAP-ENV:Subcode", NULL)); /* SOAP-ENV:Value */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "SOAP-ENV:Value", fault_data->soap_env_subcode_1, NULL)); if (fault_data->soap_env_subcode_2) { /* SOAP-ENV:Subcode begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "SOAP-ENV:Subcode", NULL)); /* SOAP-ENV:Value */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "SOAP-ENV:Value", fault_data->soap_env_subcode_2, NULL)); /* SOAP-ENV:Subcode end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "SOAP-ENV:Subcode")); } /* SOAP-ENV:Subcode end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "SOAP-ENV:Subcode")); } /* SOAP-ENV:Code end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "SOAP-ENV:Code")); /* SOAP-ENV:Reason begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "SOAP-ENV:Reason", NULL)); /* SOAP-ENV:Text */ snprintf(attr, sizeof(attr), "xml:lang=\"en\""); SOAP_IF_FAIL_RET(soap_element(xml_buf, "SOAP-ENV:Text", fault_data->soap_env_reason, attr)); /* SOAP-ENV:Reason end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "SOAP-ENV:Reason")); /* SOAP-ENV:Fault end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, G_SOAP_ENV_FAULT_STR)); return OK; } /****************************************************************************** * 函数名称: soap_fault() * 函数描述: 填充错误信息结构体内容 * 输 入: soap -- soap结构体地址 * code -- 错误码 * subcode1 -- 子错误码1 * subcode2 -- 子错误码2 * reason -- 错误原因 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ S32 soap_fault(SOAP_CONTEXT *soap, char *code, char *subcode1, char *subcode2, char *reason) { ONVIF_TRACE("Onvif_fault\n"); if (soap == NULL || code == NULL || subcode1 == NULL) { ONVIF_TRACE("soap == NULL || code == NULL || subcode1 == NULL."); return ERROR; } if (soap->fault) { ONVIF_FREE(soap->fault->soap_env_value); ONVIF_FREE(soap->fault->soap_env_subcode_1); ONVIF_FREE(soap->fault->soap_env_subcode_2); ONVIF_FREE(soap->fault->soap_env_reason); } else { soap->fault = ONVIF_MALLOC(sizeof(SOAP_ENV_FAULT)); } if (NULL == soap->fault) { ONVIF_WARN("ONVIF_MALLOC fault failed."); return ERROR; } memset(soap->fault, 0, sizeof(SOAP_ENV_FAULT)); soap->fault->soap_env_value = ONVIF_STRDUP(code); if (NULL == soap->fault->soap_env_value) { ONVIF_WARN("ONVIF_STRDUP soap_env_value failed."); return ERROR; } soap->fault->soap_env_subcode_1 = ONVIF_STRDUP(subcode1); if (NULL == soap->fault->soap_env_subcode_1) { ONVIF_WARN("ONVIF_STRDUP soap_env_subcode_1 failed."); return ERROR; } if (subcode2) { soap->fault->soap_env_subcode_2 = ONVIF_STRDUP(subcode2); if (NULL == soap->fault->soap_env_subcode_2) { ONVIF_WARN("ONVIF_STRDUP soap_env_subcode_2 failed."); return ERROR; } } if (reason) { soap->fault->soap_env_reason = ONVIF_STRDUP(reason); if (NULL == soap->fault->soap_env_reason) { ONVIF_WARN("ONVIF_STRDUP soap_env_reason failed."); return ERROR; } } return OK; } /****************************************************************************** * 函数名称: soap_generate_fault() * 函数描述: 组装错误报文xml内容 * 输 入: soap -- soap结构体地址 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_generate_fault(SOAP_CONTEXT *soap) { if (soap == NULL) { ONVIF_WARN("soap == NULL."); return ERROR; } //soap_set_fault(soap); if (soap->fault == NULL) { return ERROR; } if (soap->error < 200 && soap->error != SOAP_FAULT) { soap->has_header = FALSE; } return soap_generate_xml((p_out_fun)(soap_out_env_fault), soap, soap->fault); } /****************************************************************************** * 函数名称: soap_serve_request() * 函数描述: soap请求处理函数入口 * 输 入: soap -- soap结构体地址 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ S32 soap_serve_request(SOAP_CONTEXT *soap) { U32 index = 0; PASSTHROUGH_RET ret = 0; if (soap == NULL || soap->tag[0] == '\0') { return ERROR; } ret = onvif_serve_passthrough(soap); if (PASSTHROUGH_ERROR == ret) { return ERROR; } else if (PASSTHROUGH_SOAP_FAULT == ret) { SOAP_IF_FAIL_RET(soap_generate_fault(soap)); } else if (PASSTHROUGH_NOT_MATCH == ret) { for (index = 0; index < SOAP_TAG_NUM; index++) { if (NULL == g_soap_tag_handle[index].handle) { SOAP_IF_FAIL_RET(soap_fault(soap, "SOAP-ENV:Receiver", "ter:ActionNotSupported", NULL, NULL)); soap->error = SOAP_FAULT; SOAP_IF_FAIL_RET(soap_generate_fault(soap)); break; } if (soap_match_tag(soap->tag, g_soap_tag_handle[index].tag)) { if (g_soap_tag_handle[index].handle(soap)) { if (soap->fault == NULL) { soap_fault(soap, "SOAP-ENV:Sender", "ter:InvalidArgVal", NULL, "error"); soap->error = SOAP_FAULT; } SOAP_IF_FAIL_RET(soap_generate_fault(soap)); } break; } } if (index >= SOAP_TAG_NUM) { SOAP_IF_FAIL_RET(soap_fault(soap, "SOAP-ENV:Receiver", "ter:ActionNotSupported", NULL, NULL)); soap->error = SOAP_FAULT; SOAP_IF_FAIL_RET(soap_generate_fault(soap)); } } /* UDP直接回复,HTTP在上一层回复 */ if (soap->use_udp == TRUE && soap->xml_buf.start != NULL && soap->xml_buf.start != soap->xml_buf.last) { return onvif_send_udp_packet(soap->sock, soap->in_ip, soap->sin_port, soap->xml_buf.start, (soap->xml_buf.last - soap->xml_buf.start)); } return OK; } LOCAL S32 soap_env_fault_handle(SOAP_CONTEXT *soap) { if (soap == NULL) { ONVIF_WARN("soap == NULL."); return ERROR; } ONVIF_TRACE("SOAP-ENV:Fault handle."); /* do nothing and no response */ return OK; } /****************************************************************************** * 函数名称: soap_tag_handle_add() * 函数描述: 注册soap请求处理函数 * 输 入: tag -- 请求tag * handle -- 处理函数 * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ void soap_tag_handle_add(char *tag, void *handle) { U32 index = 0; if ((NULL == tag) || (strlen(tag) >= 64) || (NULL == handle)) { ONVIF_ERROR("soap handle add error, invalid arg."); return; } while ((index < SOAP_TAG_NUM) && (NULL != g_soap_tag_handle[index].handle)) { index++; } if (index >= SOAP_TAG_NUM) { ONVIF_ERROR("soap handle add error, max support %d tags.", SOAP_TAG_NUM); return; } snprintf(g_soap_tag_handle[index].tag, sizeof(g_soap_tag_handle[index].tag), "%s", tag); g_soap_tag_handle[index].handle = handle; return; } /****************************************************************************** * 函数名称: soap_tag_handle_init() * 函数描述: 初始化soap请求处理函数 * 输 入: N/A * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ void soap_tag_handle_init() { U32 index = 0; for (index = 0; index < SOAP_TAG_NUM; index++) { memset(g_soap_tag_handle[index].tag, 0, sizeof(g_soap_tag_handle[index].tag)); g_soap_tag_handle[index].handle = NULL; } soap_tag_handle_add("SOAP-ENV:Fault", soap_env_fault_handle); return; } ——+能不能详细地一行一行一个一个变量一个一个函数地给我解释下代码
最新发布
09-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值