【问题标题】:Building a SOAP client in Java用 Java 构建 SOAP 客户端
【发布时间】:2017-05-23 12:56:16
【问题描述】:

我想在 Java 中使用一个 SOAP API,它接收像这样的 XML 来调用一个方法:

<?xml version="1.0" encoding="utf-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <env:Body>
        <GetReturnAnalysis xmlns="http://www.address.com/integration">
            <entityCode>186D3CAD-0841</entityCode>
        </GetReturnAnalysis>
    </env:Body>
</env:Envelope>

为了做到这一点,我创建了以下类:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "GetReturnAnalysis")
public class GetReturnAnalysisRequest {
  @XmlElement(name = "entityCode")
  protected String entityCode;
  @XmlAttribute(name="xmlns", required = true)
  public final static String xmlns="http://www.address.com/integration";

  public GetReturnAnalysisRequest(String entityCode) {
    this.entityCode = entityCode;
  }

  public GetReturnAnalysisRequest() { }

  public String getEntityCode() {
    return entityCode;
  }

  public void setEntityCode(String entityCode) {
    this.entityCode = entityCode;
  }        
}

并制作了以下代码来构建要发送的消息:

private  SOAPMessage createSOAPRequest(GetReturnAnalysis request) throws SOAPException, JAXBException, IOException, ParserConfigurationException, SAXException {
    MessageFactory messageFactory = MessageFactory.newInstance("SOAP 1.2 Protocol");
    SOAPMessage message = messageFactory.createMessage();
    SOAPPart soapPart = message.getSOAPPart();
    SOAPEnvelope envelope = soapPart.getEnvelope();
    envelope.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
    envelope.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema");
    message.getSOAPHeader().detachNode();

    SOAPBody body = envelope.getBody();
    String requestString =  XmlHelper.toOutputString(request);
    Document doc = convertStringToDocument(requestString);
    body.addDocument(doc);
    message.writeTo(System.out);

    message.saveChanges();
    message.writeTo(System.out);


    return message;
  }

(内部调用的方法,在这段代码中):

public static <T> String toOutputString(T type) throws IOException {
    Validate.notNull(type, "Java type not defined!");

    try {
      StringWriter os = new StringWriter();
      JAXBContext jaxbContext = JAXBContext.newInstance(type.getClass());
      Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

      // output pretty printed
      jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
      jaxbMarshaller.marshal(type, os);

      System.out.println(os.toString());

      return os.toString();
    } catch (JAXBException e) {
      throw new IOException(e);
    }
  }




private Document convertStringToDocument(String xml) throws SAXException, IOException, ParserConfigurationException {
    return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
  }

在我看来,它应该可以工作。但是,它会生成一个像这样的 XML:

<?xml version="1.0" encoding="utf-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <env:Body>
        <GetReturnAnalysis xmlns="">
            <entityCode>186D3CAD-0841</entityCode>
        </GetReturnAnalysis>
    </env:Body>
</env:Envelope>

而且服务器不接受,因为它没有填充 xmlns="http://www.address.com/integration" 属性。我想知道是不是因为属性的名称(xmlns)...但是,这是服务器期望的名称,我无法更改它,因为它是第三方 API。

我也尝试过不同的方式,像这样声明类:

  @XmlRootElement(name = "GetReturnAnalysis", namespace = "http://www.address.com/integration")
  public class GetReturnAnalysisRequest { ...

但是当我添加到消息中时(body.addDocument 方法),它检索到一个错误(那里不应该有命名空间)。

在代码中,您可能已经注意到,我放置了两个 message.writeTo(用于调试,在 createSOAPRequest 方法上)。第一个正确地给了我 XML,第二个,在我调用“保存”之后,我在 GetReturnAnalysis 上得到了 xmlns 属性为空的 xml。

我想知道你是否可以帮助我。我是 SOAP 的菜鸟,在这个问题上遇到了很多麻烦......

更新 1

我已使用邮递员成功发送了一条消息,其中包含以下 XML:

<?xml version="1.0" encoding="utf-8"?>
<env:Envelope xmlns="http://www.address.com/integration" xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <env:Body>
        <GetReturnAnalysis >
            <entityCode>186D3CAD-0841</entityCode>
        </GetReturnAnalysis>
    </env:Body>
</env:Envelope>

所以我对代码做了一点小改动,改变了类

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "GetReturnAnalysis")
public class GetReturnAnalysisRequest {
  @XmlElement(name = "entityCode")
  protected String entityCode;

  public GetReturnAnalysisRequest(String entityCode) {
    this.entityCode = entityCode;
  }

  public GetReturnAnalysisRequest() { }

  public String getEntityCode() {
    return entityCode;
  }

  public void setEntityCode(String entityCode) {
    this.entityCode = entityCode;
  }        
}

以及创建 SOAP 请求方法:

private  SOAPMessage createSOAPRequest(GetReturnAnalysis request) throws SOAPException, JAXBException, IOException, ParserConfigurationException, SAXException {
    MessageFactory messageFactory = MessageFactory.newInstance("SOAP 1.2 Protocol");
    SOAPMessage message = messageFactory.createMessage();
    SOAPPart soapPart = message.getSOAPPart();
    SOAPEnvelope envelope = soapPart.getEnvelope();
    envelope.addNamespaceDeclaration("", "http://www.address.com/integration");
    envelope.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
    envelope.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema");
    message.getSOAPHeader().detachNode();

    SOAPBody body = envelope.getBody();
    String requestString =  XmlHelper.toOutputString(request);
    Document doc = convertStringToDocument(requestString);
    body.addDocument(doc);
    message.writeTo(System.out);

    message.saveChanges();
    message.writeTo(System.out);    

    return message;
  }

但是,由于某种神秘的原因,当我调用 message.saveChanges 时,GetReturnAnalysis 类的结果如下:

<GetReturnAnalysis xmlns="">
    <EntityCode>186D3CAD-0841</EntityCode>
</GetReturnAnalysis>ityCode>

并且空的 xmlns 属性覆盖了我提供的整个命名空间。我想知道为什么会这样?难道不能简单地保存我要保存的字符串,而不做任何更改吗?

【问题讨论】:

  • 你必须定义请求对象的命名空间。但我建议经历整个 WS 生命周期。从拥有 WSDL 开始,生成客户端类和消息对象,并使用对象级 API 调用或公开 Web 服务。我建议使用一些成熟的框架,例如 Axis2 或 CXF(我的首选)。
  • 你能发布错误/异常吗?
  • 响应,当我使用空属性调用时:响应 SOAP 消息 ...<value>soap:Sender</value><reason><text xml:lang="en">System.Web.Services.Protocols.SoapException: 无法处理没有有效操作参数的请求。请提供有效的肥皂动作。在 System.Web.Services.Protocols.Soap12ServerProtocolHelper.RouteRequest() 在 System.Web.Services.Protocols.SoapServerProtocol.Initialize() ... </text></reason><detail></detail>...
  • 异常消息,当我尝试将命名空间放在类上时:NAMESPACE_ERR:尝试以不正确的命名空间方式创建或更改对象。
  • @gusto2 我正在搜索如何为该对象定义命名空间,但我还没有找到。我相信这会为我解决它...

标签: java xml web-services soap jaxb


【解决方案1】:

您的代码中有几个问题

命名空间定义:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "GetReturnAnalysis", namespace = "http://www.address.com/integration")
public class GetReturnAnalysisRequest {
  @XmlElement(name = "entityCode", namespace = "http://www.address.com/integration")
  protected String entityCode;

  public GetReturnAnalysisRequest(String entityCode) {
    this.entityCode = entityCode;
  }

  public GetReturnAnalysisRequest() { }

  public String getEntityCode() {
    return entityCode;
  }

  public void setEntityCode(String entityCode) {
    this.entityCode = entityCode;
  }   
}

第二 - 将文档构建器设置为命名空间感知

String requestString =  toOutputString(request);

Document doc = convertStringToDocument(requestString);
body.addDocument(doc);
message.writeTo(System.out);

-

private Document convertStringToDocument(String xml) throws SAXException, IOException, ParserConfigurationException {
        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        docFactory.setNamespaceAware(true);
    return docFactory.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
  }

使用框架

要真正使用 Web 服务,您应该使用框架。我建议CXF(我的首选)或Axis2。然后,您可以定义服务接口并生成适当的 wsdl 和客户端类,并获得对其他可选协议和扩展(例如安全性)的支持。

我只建议在特殊情况下直接处理 XML,例如 RPC 文字、自定义安全性或其他不再支持的协议。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-21
    • 1970-01-01
    • 1970-01-01
    • 2019-07-22
    • 1970-01-01
    • 1970-01-01
    • 2010-09-16
    相关资源
    最近更新 更多