概述
SOAP Handler是一個SOAP message的攔截器,它可以攔截進來或出去兩個方向的SOAP message,修改並決定是否放行。
例如:
在服務端啟用一個handler,攔截請求的message,檢查是否包含指定的head參數;包含的放行,不包含的以異常作為響應。在客戶端啟用一個handler,攔截發出的請求message,向其中添加指定的head參數。
其實現如下文。
服務端
文件分布圖

說明:這里使用了Maven的結構,將java文件和xml文件分別放置在src/main/java和src/main/resources兩個源文件夾下。
Handler
創建一個handler攔截所有請求的message,嘗試從head中獲取用戶信息;獲取成功就放行,否則以拋出異常作為響應。
handler需要實現javax.xml.ws.handler.soap.SOAPHandler接口。
AccessHandler.java
package cn.ljl.sand.jws.chapter4.service.handler; import java.util.Set; import javax.xml.namespace.QName; import javax.xml.soap.MessageFactory; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPFault; import javax.xml.soap.SOAPHeader; import javax.xml.soap.SOAPMessage; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; import javax.xml.ws.soap.SOAPFaultException; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class AccessHandler implements SOAPHandler<SOAPMessageContext> { @Override public boolean handleMessage(SOAPMessageContext context) { Boolean out = (Boolean) context .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); // true表示出的方向,即服務端返回的時候;false表示進的方向,即服務端接收請求參數的時候 if (!out) { SOAPHeader header = null; try { header = context.getMessage().getSOAPPart().getEnvelope().getHeader(); } catch (SOAPException e) { e.printStackTrace(); return false; } if (header == null) { String tip = "缺少頭部信息!"; SOAPFaultException exception = createFaultException(tip); throw exception; } else if (!header.hasChildNodes()) { String tip = "頭部信息不能為空!"; SOAPFaultException exception = createFaultException(tip); throw exception; } else { NodeList nl = header.getElementsByTagNameNS( "http://service.chapter4.jws.sand.ljl.cn/", "user"); if (nl.getLength() == 0) { String tip = "頭部信息中找不到用戶信息!"; SOAPFaultException exception = createFaultException(tip); throw exception; } Node node = nl.item(0); String user = node.getTextContent(); System.out.println("請求的用戶為:" + user); } } return true; } @Override public boolean handleFault(SOAPMessageContext context) { return false; } @Override public void close(MessageContext context) { } @Override public Set<QName> getHeaders() { return null; } /** * 根據消息創建異常. * * @param message * 消息 * @return */ private SOAPFaultException createFaultException(String message) { SOAPFault fault = null; try { SOAPMessage smess = MessageFactory.newInstance().createMessage(); SOAPBody body = smess.getSOAPPart().getEnvelope().getBody(); fault = body.addFault(); fault.setFaultString(message); } catch (SOAPException e) { e.printStackTrace(); if (fault == null) return null; } SOAPFaultException exception = new SOAPFaultException(fault); return exception; } }
Handler配置文件
Handler就是一個過濾器,配置文件可以將多個Handler組裝成一個鏈,不同的配置文件可以不同的方式組裝,如此就實現了代碼的重用和靈活的裝配。
Handler配置文件是一個xml文件。
handler-chains.xml
<?xml version="1.0" encoding="UTF-8"?> <javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <javaee:handler-chain> <javaee:handler> <javaee:handler-class> cn.ljl.sand.jws.chapter4.service.handler.AccessHandler </javaee:handler-class> </javaee:handler> </javaee:handler-chain> </javaee:handler-chains>
服務接口
定義web服務的接口,不需要任何參數,返回一個字符串作為成功的標志;能被服務接收的請求都是成功的。
IAccessService.java
package cn.ljl.sand.jws.chapter4.service; import javax.jws.WebResult; import javax.jws.WebService; @WebService public interface IAccessService { @WebResult(name = "accessResult") public String access(); }
服務實現類
實現上面定義的接口,返回一個成功的字符串;使用@HandlerChain注解,指定Handler配置文件,此后請求該服務的消息將被配置文件中裝配的過濾鏈過濾。
AccessServiceImpl.java
package cn.ljl.sand.jws.chapter4.service; import javax.jws.HandlerChain; import javax.jws.WebService; @WebService(endpointInterface = "cn.ljl.sand.jws.chapter4.service.IAccessService") @HandlerChain(file = "cn/ljl/sand/jws/chapter4/service/handler/handler-chains.xml") public class AccessServiceImpl implements IAccessService { @Override public String access() { String message = "你成功了!"; return message; } }
服務發布者
定義一個類,發布服務。
AccessServicePublisher.java
package cn.ljl.sand.jws.chapter4.service; import javax.xml.ws.Endpoint; public class AccessServicePublisher { public static void main(String[] args) { String address = "http://localhost:6666/service/access"; IAccessService service = new AccessServiceImpl(); Endpoint.publish(address, service); } }
發布服務
運行AccessServicePublisher的main,發布服務,wsdl地址:http://localhost:6666/service/access?wsdl
客戶端
文件分布圖

說明:src/main/java:cn.ljl.sand.jws.chapter4.client.wsimport中的文件,都是使用wsimport生成的,不做詳細介紹。
Handler
創建一個Handler,攔截所有發出的請求message,往其中添加head參數,然后放行。
UserHandler.java
package cn.ljl.sand.jws.chapter4.client.handler; import java.util.Set; import javax.xml.namespace.QName; import javax.xml.soap.SOAPEnvelope; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPFault; import javax.xml.soap.SOAPHeader; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; public class UserHandler implements SOAPHandler<SOAPMessageContext> { @Override public boolean handleMessage(SOAPMessageContext context) { Boolean out = (Boolean) context .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); // true表示出的方向,即發送請求到服務端 if (out) { try { SOAPEnvelope envelope = context.getMessage().getSOAPPart() .getEnvelope(); SOAPHeader header = envelope.getHeader(); if (header == null) header = envelope.addHeader(); QName hname = new QName( "http://service.chapter4.jws.sand.ljl.cn/", "user"); header.addChildElement(hname).setTextContent("楊過"); } catch (Exception e) { e.printStackTrace(); return false; } } return true; } @Override public boolean handleFault(SOAPMessageContext context) { SOAPFault fault = null; try { SOAPEnvelope envelope = context.getMessage().getSOAPPart() .getEnvelope(); fault = envelope.getBody().getFault(); } catch (SOAPException e) { e.printStackTrace(); return false; } System.out.println("在客戶端Handler中:" + fault.getFaultString()); return false; } @Override public void close(MessageContext context) { // TODO Auto-generated method stub } @Override public Set<QName> getHeaders() { // TODO Auto-generated method stub return null; } }
Handler配置文件
handler-chains.xml
-
<?xml version="1.0" encoding="UTF-8"?> <javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <javaee:handler-chain> <javaee:handler> <javaee:handler-class> cn.ljl.sand.jws.chapter4.client.handler.UserHandler </javaee:handler-class> </javaee:handler> </javaee:handler-chain> </javaee:handler-chains>
使用wsimport生成代碼
指定-p cn.ljl.sand.jws.chapter4.client.wsimport
修改生成AccessServiceImplService.java,為類添加注解:
@HandlerChain(file = "cn/ljl/sand/jws/chapter4/client/handler/handler-chains.xml")
客戶端
創建一個測試類,發起請求。
WSIClient.java
package cn.ljl.sand.jws.chapter4.client; import java.net.MalformedURLException; import java.net.URL; import org.junit.Assert; import org.junit.Test; import cn.ljl.sand.jws.chapter4.client.wsimport.AccessServiceImplService; import cn.ljl.sand.jws.chapter4.client.wsimport.IAccessService; public class WSIClient { /** wsdl的地址 */ private static final String WSDL_URL = "http://localhost:6666/service/access?wsdl"; @Test public void test() throws MalformedURLException { URL url = new URL(WSDL_URL); AccessServiceImplService ss = new AccessServiceImplService(url); IAccessService service = ss.getAccessServiceImplPort(); String message = service.access(); Assert.assertEquals("你成功了!", message); } }
測試
客戶端不添加head參數
將
AccessServiceImplService.java我們添加的注解去掉,進行測試:

客戶端添加head參數
在AccessServiceImplService.java中添加
@HandlerChain注解
,進行測試:
