我试图使用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)的答案,这个答案扩展了这一点