Làm cách nào để thực hiện cuộc gọi Dịch vụ web SOAP từ lớp Java?


116

Tôi là người mới đối với thế giới dịch vụ web và nghiên cứu của tôi dường như làm tôi bối rối hơn là khai sáng cho tôi, vấn đề của tôi là tôi đã được cấp một thư viện (jar) mà tôi phải mở rộng với một số chức năng dịch vụ web.

Thư viện này sẽ được chia sẻ cho các nhà phát triển khác và trong số các lớp trong jar sẽ là các lớp có phương thức gọi dịch vụ web (về cơ bản đặt thuộc tính của lớp, một số logic nghiệp vụ, như lưu trữ đối tượng trong db, vv và gửi lại đối tượng với những sửa đổi). Tôi muốn thực hiện cuộc gọi đến dịch vụ này đơn giản nhất có thể, hy vọng đơn giản để nhà phát triển sử dụng lớp chỉ cần thực hiện.

Car c = new Car("Blue");
c.webmethod();

Tôi đã nghiên cứu JAX-WS để sử dụng trên máy chủ nhưng dường như tôi không cần tạo wsimportmáy chủ cũng như wsimportmáy khách, vì tôi biết rằng cả hai đều có các lớp, tôi chỉ cần một số tương tác giữa các lớp chia sẻ trong cả máy chủ và máy khách. Làm thế nào để bạn nghĩ có ý nghĩa để làm dịch vụ web và cuộc gọi trong lớp?


Câu hỏi của bạn là một chút không rõ ràng. Phương thức bạn muốn tạo sẽ (1) lấy đối tượng từ dịch vụ web; (2) làm việc với đối tượng một chút; và (3) gửi lại cho dịch vụ web. Là nó?
acdcjunior

Không, đối tượng sẽ được tạo trong máy khách, nó sẽ được gửi đến ws trong cuộc gọi, ws sẽ đặt một biến, ví dụ currentTime, thực hiện một số logic nghiệp vụ như lưu trữ nó trong db và sau đó gửi đối tượng trở lại máy khách với currentTime hiện được đặt. Hy vọng tôi giải thích bản thân mình tốt hơn một chút. Cảm ơn bạn.
jpz

Câu trả lời:


273

Tôi hiểu vấn đề của bạn tập trung vào cách gọi một dịch vụ web SOAP (JAX-WS) từ Java và nhận đối tượng trả về của nó . Trong trường hợp đó, bạn có hai cách tiếp cận có thể:

  1. Tạo các lớp Java thông qua wsimportvà sử dụng chúng; hoặc là
  2. Tạo một ứng dụng khách SOAP:
    1. Nối tiếp các tham số của dịch vụ thành XML;
    2. Gọi phương thức web thông qua thao tác HTTP; và
    3. Phân tích phản hồi XML trở lại thành một đối tượng.


Về cách tiếp cận đầu tiên (sử dụng wsimport):

Tôi thấy bạn đã có các lớp nghiệp vụ (thực thể hoặc khác) của các dịch vụ và thực tế là nó wsimporttạo ra một tập hợp các lớp hoàn toàn mới (đó là một bản sao nào đó của các lớp bạn đã có).

Tuy nhiên, tôi sợ, trong kịch bản này, bạn chỉ có thể:

  • Điều chỉnh (chỉnh sửa) wsimportmã được tạo để làm cho nó sử dụng các lớp nghiệp vụ của bạn (điều này rất khó và bằng cách nào đó không có giá trị - hãy nhớ rằng mỗi khi WSDL thay đổi, bạn sẽ phải tạo lại và đọc lại mã); hoặc là
  • Từ bỏ và sử dụng các wsimportlớp được tạo ra. (Trong giải pháp này, mã doanh nghiệp của bạn có thể "sử dụng" các lớp được tạo làm dịch vụ từ một lớp kiến ​​trúc khác.)

Về cách tiếp cận thứ hai (tạo ứng dụng khách SOAP tùy chỉnh của bạn):

Để thực hiện phương pháp thứ hai, bạn sẽ phải:

  1. Thuc hien cuoc goi:
    • Sử dụng khung SAAJ (SOAP với API đính kèm cho Java) (xem bên dưới, nó được phân phối với Java SE 1.6 trở lên) để thực hiện các cuộc gọi; hoặc là
    • Bạn cũng có thể làm điều đó thông qua java.net.HttpUrlconnection(và một số java.ioxử lý).
  2. Biến các đối tượng thành và quay lại từ XML:
    • Sử dụng khung OXM (Ánh xạ đối tượng thành XML) như JAXB để tuần tự hóa / giải tuần tự hóa XML từ / thành các đối tượng
    • Hoặc, nếu bạn phải, tự tạo / phân tích cú pháp XML (đây có thể là giải pháp tốt nhất nếu đối tượng nhận được chỉ khác một chút so với đối tượng đã gửi).

Tạo một ứng dụng khách SOAP bằng cách sử dụng cổ điển java.net.HttpUrlConnectionkhông khó lắm (nhưng cũng không đơn giản) và bạn có thể tìm thấy trong liên kết này một mã khởi đầu rất tốt.

Tôi khuyên bạn nên sử dụng khung SAAJ:

SOAP với API đính kèm cho Java (SAAJ) chủ yếu được sử dụng để xử lý trực tiếp các thông báo Yêu cầu / Phản hồi SOAP xảy ra sau hậu trường trong bất kỳ API dịch vụ web nào. Nó cho phép các nhà phát triển trực tiếp gửi và nhận tin nhắn xà phòng thay vì sử dụng JAX-WS.

Xem bên dưới một ví dụ hoạt động (chạy nó!) Của một cuộc gọi dịch vụ web SOAP bằng SAAJ. Nó gọi dịch vụ web này .

import javax.xml.soap.*;

public class SOAPClientSAAJ {

    // SAAJ - SOAP Client Testing
    public static void main(String args[]) {
        /*
            The example below requests from the Web Service at:
             https://www.w3schools.com/xml/tempconvert.asmx?op=CelsiusToFahrenheit


            To call other WS, change the parameters below, which are:
             - the SOAP Endpoint URL (that is, where the service is responding from)
             - the SOAP Action

            Also change the contents of the method createSoapEnvelope() in this class. It constructs
             the inner part of the SOAP envelope that is actually sent.
         */
        String soapEndpointUrl = "https://www.w3schools.com/xml/tempconvert.asmx";
        String soapAction = "https://www.w3schools.com/xml/CelsiusToFahrenheit";

        callSoapWebService(soapEndpointUrl, soapAction);
    }

    private static void createSoapEnvelope(SOAPMessage soapMessage) throws SOAPException {
        SOAPPart soapPart = soapMessage.getSOAPPart();

        String myNamespace = "myNamespace";
        String myNamespaceURI = "https://www.w3schools.com/xml/";

        // SOAP Envelope
        SOAPEnvelope envelope = soapPart.getEnvelope();
        envelope.addNamespaceDeclaration(myNamespace, myNamespaceURI);

            /*
            Constructed SOAP Request Message:
            <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:myNamespace="https://www.w3schools.com/xml/">
                <SOAP-ENV:Header/>
                <SOAP-ENV:Body>
                    <myNamespace:CelsiusToFahrenheit>
                        <myNamespace:Celsius>100</myNamespace:Celsius>
                    </myNamespace:CelsiusToFahrenheit>
                </SOAP-ENV:Body>
            </SOAP-ENV:Envelope>
            */

        // SOAP Body
        SOAPBody soapBody = envelope.getBody();
        SOAPElement soapBodyElem = soapBody.addChildElement("CelsiusToFahrenheit", myNamespace);
        SOAPElement soapBodyElem1 = soapBodyElem.addChildElement("Celsius", myNamespace);
        soapBodyElem1.addTextNode("100");
    }

    private static void callSoapWebService(String soapEndpointUrl, String soapAction) {
        try {
            // Create SOAP Connection
            SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
            SOAPConnection soapConnection = soapConnectionFactory.createConnection();

            // Send SOAP Message to SOAP Server
            SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(soapAction), soapEndpointUrl);

            // Print the SOAP Response
            System.out.println("Response SOAP Message:");
            soapResponse.writeTo(System.out);
            System.out.println();

            soapConnection.close();
        } catch (Exception e) {
            System.err.println("\nError occurred while sending SOAP Request to Server!\nMake sure you have the correct endpoint URL and SOAPAction!\n");
            e.printStackTrace();
        }
    }

    private static SOAPMessage createSOAPRequest(String soapAction) throws Exception {
        MessageFactory messageFactory = MessageFactory.newInstance();
        SOAPMessage soapMessage = messageFactory.createMessage();

        createSoapEnvelope(soapMessage);

        MimeHeaders headers = soapMessage.getMimeHeaders();
        headers.addHeader("SOAPAction", soapAction);

        soapMessage.saveChanges();

        /* Print the request message, just for debugging purposes */
        System.out.println("Request SOAP Message:");
        soapMessage.writeTo(System.out);
        System.out.println("\n");

        return soapMessage;
    }

}

Về việc sử dụng JAXB để tuần tự hóa / giải tuần tự hóa, rất dễ dàng để tìm thấy thông tin về nó. Bạn có thể bắt đầu ở đây: http://www.mkyong.com/java/jaxb-hello-world-example/ .


Làm cách nào để đặt phiên bản xà phòng bằng phương pháp được đề cập ở trên?
Làm lại

Tôi đã có thể sử dụng phương thức của bạn và nó hoạt động khi tôi sử dụng URI của bạn nhưng đối với yêu cầu SOAP của riêng tôi, tôi nhận được phản hồi theo đó không có giá trị nào được hiển thị như mong đợi, nghĩa là <xsd:element name="Incident_Number" type="xsd:string"/>. Như bạn có thể thấy, phần tử được đóng và không có thông tin nào được tạo từ WS.
Martin Erlic

Các GetInfoByCity503Service Unavailable, nó seeems. :(
Brad Turek

@BradTurek D * mn! Tôi chỉ cần thay thế nó. Cảm ơn vì đã cho tôi biết! Tôi sẽ tìm một cái khác và thay đổi nó một chút.
acdcjunior

1
Đối với người qua đường: Nếu đoạn mã trên (ví dụ về điểm cuối của Dịch vụ web SOAP) ngừng hoạt động hoặc bắt đầu đưa ra các lỗi (như 500, 503, v.v.), vui lòng cho tôi biết để tôi có thể sửa nó.
acdcjunior

3

Hoặc chỉ sử dụng wsdl2java của Apache CXF để tạo các đối tượng bạn có thể sử dụng.

Nó được bao gồm trong gói nhị phân mà bạn có thể tải xuống từ trang web của họ. Bạn chỉ có thể chạy một lệnh như thế này:

$ ./wsdl2java -p com.mynamespace.for.the.api.objects -autoNameResolution http://www.someurl.com/DefaultWebService?wsdl

Nó sử dụng wsdl để tạo các đối tượng mà bạn có thể sử dụng như thế này (tên đối tượng cũng được lấy từ wsdl, vì vậy tên của bạn sẽ khác một chút):

DefaultWebService defaultWebService = new DefaultWebService();
String res = defaultWebService.getDefaultWebServiceHttpSoap11Endpoint().login("webservice","dadsadasdasd");
System.out.println(res);

Thậm chí còn có một trình cắm Maven tạo ra các nguồn: https://cxf.apache.org/docs/maven-cxf-codegen-plugin-wsdl-to-java.html

Lưu ý: Nếu bạn tạo nguồn bằng CXF và IDEA, bạn có thể muốn xem xét điều này: https://stackoverflow.com/a/46812593/840315


1
Tôi có hơn 30 wsdl trong ứng dụng của mình. Trong khi chuẩn bị tài nguyên cho chỉ 1 wsdl (có 5 xà phòng), IDE Eclipse của tôi đã bị treo và tạo ra khoảng hơn 100 MB lớp / đối tượng.
Manmohan_singh

-1

Tôi tìm thấy một cách khác đơn giản hơn nhiều để tạo tin nhắn xà phòng. Đưa ra một đối tượng người:

import com.fasterxml.jackson.annotation.JsonInclude;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {
  private String name;
  private int age;
  private String address; //setter and getters below
}

Dưới đây là một trình tạo tin nhắn xà phòng đơn giản:

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;

@Slf4j
public class SoapGenerator {

  protected static final ObjectMapper XML_MAPPER = new XmlMapper()
      .enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
      .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
      .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
      .registerModule(new JavaTimeModule());

  private static final String SOAP_BODY_OPEN = "<soap:Body>";
  private static final String SOAP_BODY_CLOSE = "</soap:Body>";
  private static final String SOAP_ENVELOPE_OPEN = "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">";
  private static final String SOAP_ENVELOPE_CLOSE = "</soap:Envelope>";

  public static String soapWrap(String xml) {
    return SOAP_ENVELOPE_OPEN + SOAP_BODY_OPEN + xml + SOAP_BODY_CLOSE + SOAP_ENVELOPE_CLOSE;
  }

  public static String soapUnwrap(String xml) {
    return StringUtils.substringBetween(xml, SOAP_BODY_OPEN, SOAP_BODY_CLOSE);
  }
}

Bạn có thể sử dụng bằng cách:

 public static void main(String[] args) throws Exception{
        Person p = new Person();
        p.setName("Test");
        p.setAge(12);

        String xml = SoapGenerator.soapWrap(XML_MAPPER.writeValueAsString(p));
        log.info("Generated String");
        log.info(xml);
      }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.