1. 程式人生 > >CXF系列之SOAP訊息格式

CXF系列之SOAP訊息格式

我們從前面瞭解 WebService 使用 HTTP 協議傳輸訊息,訊息格式使用 SOAP,那麼在客戶端和伺服器端傳輸的 SOAP 訊息是什麼樣子的呢?下面我們將服務端 SoapServer.java 的程式碼改為如下的形式:

package com.test.server;

import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;

import com.test.impl.service.HelloServiceImpl;

public class SoapServer1 {

	public static void main(String[] args) {
		JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
		//新增輸入攔截器
		factory.getInInterceptors().add(new LoggingInInterceptor());
		//新增輸出攔截器
		factory.getOutInterceptors().add(new LoggingOutInterceptor());
		//設定Service,注意這裡是實現類,不是介面
		factory.setServiceClass(HelloServiceImpl.class);
		//設定訪問地址
		factory.setAddress("http://localhost:8080/helloService");
		factory.create();
	}
}
我們注意到這裡將 javax.xml.ws.EndPoint 改為 CXF 特有的 API---JaxWsServerFactoryBean,並且我們對服務端工廠 Bean 的輸入攔截器集合、輸出攔截器集合中分別添加了日誌攔截器(攔截器是 CXF 的一項擴充套件功能,CXF 提供了很多攔截器實現,你也可以自己實現一種攔截器),這樣可以在 Web 服務端傳送和接收訊息時輸出資訊。

現在我們再次執行伺服器端和客戶端,你會看到控制檯輸出如下資訊:

資訊: Inbound Message
----------------------------
ID: 1
Address: http://localhost:8080/helloService
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml; charset=UTF-8
Headers: {Accept=[*/*], Cache-Control=[no-cache], connection=[keep-alive], Content-Length=[371], content-type=[text/xml; charset=UTF-8], Host=[localhost:8080], Pragma=[no-cache], SOAPAction=[""], User-Agent=[Apache CXF 3.0.10]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:selectMaxAgeCustomer xmlns:ns2="http://service.inter.test.com/"><arg0><birthday>1993-03-06T00:00:00+08:00</birthday><id>1</id><name>A</name></arg0><arg1><birthday>1993-04-06T00:00:00+08:00</birthday><id>2</id><name>B</name></arg1></ns2:selectMaxAgeCustomer></soap:Body></soap:Envelope>
--------------------------------------
十一月 24, 2016 2:58:29 下午 org.apache.cxf.services.HelloServiceImplService.HelloServiceImplPort.IHelloService
資訊: Outbound Message
---------------------------
ID: 1
Response-Code: 200
Encoding: UTF-8
Content-Type: text/xml
Headers: {}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:selectMaxAgeCustomerResponse xmlns:ns2="http://service.inter.test.com/"><return><birthday>1993-03-06T00:00:00+08:00</birthday><id>1</id><name>A</name></return></ns2:selectMaxAgeCustomerResponse></soap:Body></soap:Envelope>
--------------------------------------
Inbound Message 輸出的是伺服器端接收到的 SOAP 資訊,Outbound Message 輸出的伺服器端響應的 SOAP 資訊,SOAP 的 Headers:{}的前面是 SOAP 訊息的標識、編碼方式、MIME型別,Headers:{}熟悉 HTTP 應該很容易看懂這裡面的訊息報頭的作用,Headers:{}後面的Payload(有效負載,也叫淨荷)的 XML 就是 SOAP 訊息的真正內容,我們看到 SOAP 訊息內容被封裝為<soap:Envelope …SOAP 信封,在信封之間的內容就是 SOAP 訊息正文,這
個元素還有一個子元素<soap:Header …,如果你的某些註解的 header=true,那麼它將被放到<soap:Header …中傳輸,而不是 SOAP 訊息正文。

例如我們把服務端的 IHelloService 介面改為如下的形式:

package com.test.inter.service;

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

import com.test.bean.Customer;

@WebService
public interface IHelloService1 {

	public Customer selectMaxAgeCustomer(
			@WebParam(name = "c1", header = true) Customer c1,
			@WebParam(name = "c2") Customer c2);

	public Customer selectLongNameCustomer(Customer c1, Customer c2);
}
我們注意第一個方法的第一個引數的 header=true,也就是放在 SOAP 的訊息頭中傳輸。然
後我們重新生成客戶端的程式碼,SoapClient 的呼叫程式碼改為如下的形式:
package com.test.client;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.GregorianCalendar;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

import com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl;
import com.test.bean.Customer;

public class SoapClient1 {

	public static void main(String[] args) throws ParseException {
		JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
		factory.setAddress("http://localhost:8080/helloService");
		factory.setServiceClass(IHelloService1.class);
		Object o = factory.create();
		IHelloService1 service = (IHelloService1)o;
		
		Customer c1 = new Customer();
		c1.setId(1L);
		c1.setName("A");
		GregorianCalendar calendar = (GregorianCalendar) GregorianCalendar.getInstance();
		calendar.setTime(new SimpleDateFormat("yyyy-MM-dd").parse("1993-03-06"));
		c1.setBirthday(new XMLGregorianCalendarImpl(calendar));
		
		Customer c2 = new Customer();
		c2.setId(2L);
		c2.setName("B");
		calendar.setTime(new SimpleDateFormat("yyyy-MM-dd").parse("1993-04-06"));
		c2.setBirthday(new XMLGregorianCalendarImpl(calendar));
		
		SelectMaxAgeCustomer sma = new SelectMaxAgeCustomer();
		sma.setC2(c2);
		
		System.out.println(service.selectMaxAgeCustomer(sma,c1).getReturn().getName());
	}
}

注意:

(1)IHelloService是生成客戶端中包含的類,而不是我們自己建立的。

(2)客戶端的IHelloService的第一個方法的第一個引數是SelectMaxAgeStudent,而不是 Customer

執行之後控制檯輸出如下語句:
資訊: Inbound Message
----------------------------
ID: 1
Address: http://localhost:8080/helloService
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml; charset=UTF-8
Headers: {Accept=[*/*], Cache-Control=[no-cache], connection=[keep-alive], Content-Length=[449], content-type=[text/xml; charset=UTF-8], Host=[localhost:8080], Pragma=[no-cache], SOAPAction=[""], User-Agent=[Apache CXF 3.0.10]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><ns2:c1 xmlns:ns2="http://service.inter.test.com/"><birthday>1993-03-06T00:00:00.000+08:00</birthday><id>1</id><name>A</name></ns2:c1></soap:Header><soap:Body><ns2:selectMaxAgeCustomer xmlns:ns2="http://service.inter.test.com/"><c2><birthday>1993-04-06T00:00:00.000+08:00</birthday><id>2</id><name>B</name></c2></ns2:selectMaxAgeCustomer></soap:Body></soap:Envelope>
--------------------------------------
十一月 24, 2016 3:36:14 下午 org.apache.cxf.services.HelloServiceImplService.HelloServiceImplPort.IHelloService1
資訊: Outbound Message
---------------------------
ID: 1
Response-Code: 200
Encoding: UTF-8
Content-Type: text/xml
Headers: {}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:selectMaxAgeCustomerResponse xmlns:ns2="http://service.inter.test.com/"><return><birthday>1993-03-06T00:00:00.000+08:00</birthday><id>1</id><name>A</name></return></ns2:selectMaxAgeCustomerResponse></soap:Body></soap:Envelope>
--------------------------------------
我們注意到 Inbound Message 中的 SOAP 信封將第一個方法的第一個引數放在
<soap:Header … 中傳輸。