我试图使用JAX WS实现PayPal Express Checkout API的简单Web服务客户端。 PayPal Express Checkout API提供WSDL文件,我可以使用CXF的wsdl2java实用程序生成Java类。
从认证的原因,它要求为每个请求添加SOAP头。这个标题很简单,应该看起来像这样:
https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_ECSOAPAPIBasics#id09C3I0CF0O6
从WSDL类生成的包括ebay.apis.eblbasecomponents.CustomSecurityHeaderType类,它代表我需要添加到每个请求中的头。
所以问题是:如何将手动创建的CustomSecurityHeaderType类的实例添加到SOAP请求的头部,并考虑以下条件:
>我不是非常渴望使用com.sun。*包中的类,如答案所述:JAX-WS – Adding SOAP Headers(主要是因为不同JDK之间可能的可移植性问题)
>我不想手动将该对象编组到嵌套的javax.xml.soap.SOAPElement实例中,如下所述:
How do I add a SOAP Header using Java JAX-WS
解决方法
所以,看起来我已经找到可能的答案,而JAX-WS&来自SO的JAXB相关答案(如果有人在这些技术中有经验可以检查以下是否正确,我真的很感激):
对我来说显而易见的是在其中添加SOAP消息处理程序和SOAPMessage实例的alter头:
import javax.xml.ws.Binding; import javax.xml.ws.BindingProvider; import javax.xml.ws.handler.Handler; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.Marshaller; import javax.xml.soap.SOAPHeader; import ebay.api.paypalapi.ObjectFactory; // class generated by wsdl2java // following class is generated by wsdl2java utility Service class final PayPalAPIInterfaceService payPalService = new PayPalAPIInterfaceService(); final PayPalAPIAAInterface expressCheckoutPort = payPalService.getPayPalAPIAA(); final Binding binding = ((BindingProvider) expressCheckoutPort).getBinding(); List<Handler> handlersList = new ArrayList<Handler>(); // now,adding instance of Handler to handlersList which should do our job: // creating header instance final CustomSecurityHeaderType headerObj = new CustomSecurityHeaderType(); final UserIdPasswordType credentials = new UserIdPasswordType(); credentials.setUsername("username"); credentials.setPassword("password"); credentials.setSignature("signature"); headerObj.setCredentials(credentials); // bookmark #1 - please read explanation after code final ObjectFactory objectFactory = new ObjectFactory(); // creating JAXBElement from headerObj final JAXBElement<CustomSecurityHeaderType> requesterCredentials = objectFactory.createRequesterCredentials(headerObj); handlersList.add(new SOAPHandler<SOAPMessageContext>() { @Override public boolean handleMessage(final SOAPMessageContext context) { try { // checking whether handled message is outbound one as per Martin Strauss answer final Boolean outbound = (Boolean) context.get("javax.xml.ws.handler.message.outbound"); if (outbound != null && outbound) { // obtaining marshaller which should marshal instance to xml final Marshaller marshaller = JAXBContext.newInstance(CustomSecurityHeaderType.class).createMarshaller(); // adding header because otherwise it's null final SOAPHeader soapHeader = context.getMessage().getSOAPPart().getEnvelope().addHeader(); // marshalling instance (appending) to SOAP header's xml node marshaller.marshal(requesterCredentials,soapHeader); } } catch (final Exception e) { throw new RuntimeException(e); } return true; } // ... default implementations of other methods go here }); // as per Jean-Bernard Pellerin's comment setting handlerChain list here,after all handlers were added to list binding.setHandlerChain(handlersList);
书签#1的说明:
应该不是头文件本身,而是代表该对象的JAXBElement,否则会得到异常。应该使用从WSDL生成的一个ObjectFactory类,以从原始对象创建所需的JAXBElement实例。
(感谢@skaffman的答案:No @XmlRootElement generated by JAXB)
还应该参考马丁·斯特劳斯(Martin Straus)的答案,这个答案扩展了这一点