wsimport 生成客戶端存根
wsimport -keep -extension -s ./src -p com.jaxws.test https://192.168.1.1:8080/service?wsdl
| 參數 | 說明 |
-keep | 保留生成的 .java 原始碼 |
-extension | 允許 JAX-WS 擴展綁定 |
-s | 原始碼輸出目錄 |
-p | 指定包名 |
調用端程式碼
public String callService(Object[] args) {
ServicesService service = new ServicesService(); // 生成的 Service 類
// 註冊 SOAP Handler 攔截器
service.setHandlerResolver(new HandlerResolver() {
@Override
public List getHandlerChain(PortInfo portInfo) {
List chain = new ArrayList<>();
chain.add(new ClientHandler()); // 自定義 Header 處理
return chain;
}
});
String result = service.getServicesPort().getResults(Arrays.toString(args));
System.out.println(result);
return result;
}
SOAP Header 驗證處理器 ClientHandler
public class ClientHandler implements SOAPHandler {
@Override
public boolean handleMessage(SOAPMessageContext ctx) {
Boolean outbound = (Boolean) ctx.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) { // 僅處理出站請求
try {
SOAPMessage msg = ctx.getMessage();
SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
SOAPHeader header = env.getHeader();
if (header == null) header = env.addHeader();
// 建立驗證 Header
QName qname = new QName("https://csdc.info/", "Authentication", "wsse");
SOAPHeaderElement auth = header.addHeaderElement(qname);
SOAPElement user = auth.addChildElement("Username", "wsse");
user.addTextNode("admin");
SOAPElement pass = auth.addChildElement("Password", "wsse");
pass.addTextNode("admin");
msg.saveChanges();
msg.writeTo(System.out); // 調試輸出
return true;
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
@Override public boolean handleFault(SOAPMessageContext ctx) { return false; }
@Override public void close(MessageContext ctx) {}
@Override public Set getHeaders() { return null; }
}
生成的 SOAP 請求結構
<soap:Envelope>
<soap:Header>
<wsse:Authentication xmlns:wsse="https://csdc.info/">
<wsse:Username>admin</wsse:Username>
<wsse:Password>admin</wsse:Password>
</wsse:Authentication>
</soap:Header>
<soap:Body>...</soap:Body>
</soap:Envelope>
關鍵技術點
| 點 | 說明 |
MESSAGE_OUTBOUND_PROPERTY | 區分請求/響應,僅在請求階段加 Header |
QName(ns, local, prefix) | 正確宣告命名空間與前綴,避免伺服器解析失敗 |
msg.saveChanges() | 修改 Header 後必須調用,否則不生效 |
| 執行緒安全 | Handler 實例可共用,無狀態設計 |
現代化替代建議
- CXF:功能更豐富、Spring 整合更好、支援 JAX-RS
- Spring WebServiceTemplate:模板化調用、攔截器鏈、O/X 映射
- OpenAPI/REST:新專案優先考慮 REST + JSON,SOAP 僅用於舊系統整合