How to do a SOAP Web Service call from Java class?

I'm relative new to the webservices world and my research seems to have confused me more than enlighten me, my problem is that I was given a library(jar) which I have to extend with some webservice functionality.

This library will be shared to other developers, and among the classes in the jar will be classes that have a method which calls a webservice (that essentially sets an attribute of the class, does some business logic, like storing the object in a db, etc and sends back the object with those modifications). I want to make the call to this service as simple as possible, hopefully as simple so that the developer using the class only need to do.

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

I have been studying JAX-WS to use on the server but seems to me that I don't need to create a wsimport in the server nor the wsimport on the client, since I know that both have the classes, I just need some interaction between classes shared in both the server and the client. How do you think makes sense to do the webservice and the call in the class?

545893 次浏览

我明白你的问题归结为 如何从 Java 调用 SOAP (JAX-WS) Web 服务并获取其返回对象。在这种情况下,你有两种可能的方法:

  1. 通过 wsimport生成 Java 类并使用它们; 或者
  2. 创建一个 SOAP 客户端:
    1. 将服务的参数序列化为 XML;
    2. 通过 HTTP 操作调用 web 方法; 以及
    3. Parse the returning XML response back into an object.


关于第一种方法(使用 wsimport) :

I see you already have the services' (entities or other) business classes, and it's a fact that the wsimport generates a whole new set of classes (that are somehow duplicates of the classes you already have).

不过,在这种情况下,恐怕你只能选择:

  • 改编(编辑) wsimport生成的代码,使其使用 你的业务类(这很困难,而且不知何故不值得——请记住每次 WSDL 更改时,您都必须重新生成并重新改编代码) ; 或者
  • 放弃使用 wsimport生成的类。(在这个解决方案中,业务代码可以“使用”生成的类作为来自另一个体系结构层的服务。)

关于第二种方法(创建自定义 SOAP 客户端) :

为了实现第二种方法,你必须:

  1. 打电话:
    • 使用 SAAJ (SAAJ)框架(见下文,它与 Java SE 1.6或更高版本一起提供)进行调用; 或者
    • 您也可以通过 java.net.HttpUrlconnection(和一些 java.io处理)来完成。
  2. 将对象转换为 XML 或从 XML 转换回来:
    • 使用诸如 JAXB 之类的 OXM (Object to XML Mapping)框架将 XML 从对象序列化/反序列化为对象
    • 或者,如果必须的话,手动创建/解析 XML (如果接收到的对象与发送的对象只有一点点不同,那么这可能是最好的解决方案)。

使用经典的 java.net.HttpUrlConnection创建 SOAP 客户机并不是那么困难(但也不是那么简单) ,您可以在 这个链接中找到非常好的起始代码。

我建议您使用 SAAJ 框架:

SAAJ (SAAJ) 主要用于直接处理发生在任何 Web 服务 API 幕后的 SOAP 请求/响应消息。它允许开发人员直接发送和接收肥皂消息,而不必使用 JAX-WS。

下面是一个使用 SAAJ 的 SOAPWeb 服务调用的工作示例(运行它!) ,它调用 网上服务

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;
}


}

About using JAXB for serializing/deserializing, it is very easy to find information about it. You can start here: http://www.mkyong.com/java/jaxb-hello-world-example/.

或者仅仅使用 Apache CXF 的 wsdl2java来生成您可以使用的对象。

它包含在二进制软件包中,你可以从他们的网站下载。您可以简单地运行如下命令:

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

它使用 wsdl 来生成对象,您可以像使用 this一样使用它(对象名称也是从 wsdl 中获取的,因此您的对象名称会有一点不同) :

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

甚至还有一个生成源代码的 Maven 插件: https://cxf.apache.org/docs/maven-cxf-codegen-plugin-wsdl-to-java.html

注意: 如果您使用 CXF 和 IDEA 生成源代码,您可能想看看以下内容: https://stackoverflow.com/a/46812593/840315

我找到了一种更简单的生成肥皂信息的方法。 给定一个人物对象:

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
}

下面是一个简单的肥皂消息生成器:

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);
}
}

你可以使用:

 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);
}

对于将 xml 请求作为字符串的人可能有帮助。 if you have WSDL, You can create a new soap request in SoapUI with that WSDL file.
它会自动为输入请求生成结构/XML。

如果您有来自 SoapUI 的输入请求 xml,可以使用下面这些简单的 Java 代码来调用 Soap 服务:

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;


public class SimpleSoapClient {


public static void main(String args[]) throws IOException {
        

String address="Hyderabad";


/* place your xml request from soap ui below with necessary changes in parameters*/
    

String xml="<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ws=\"http://www.YourUrlAsPerWsdl.com/\">\r\n" +
"   <soapenv:Header/>\r\n" +
"   <soapenv:Body>\r\n" +
"      <ws:callRest>\r\n" +
"         <name>"+"Hello"+"</name>\r\n" +
"         <address>"+address+"</address>\r\n" +
"      </ws:callRest>\r\n" +
"   </soapenv:Body>\r\n" +
"</soapenv:Envelope>";
String responseF=callSoapService(xml);
System.out.println(responseF);
}
    



}


static String callSoapService(String soapRequest) {
try {
String url = "https://gogle.com/service/hello"; // replace your URL here
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
     

// change these values as per soapui request on top left of request, click on RAW, you will find all the headers
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type","text/xml; charset=utf-8");
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(soapRequest);
wr.flush();
wr.close();
String responseStatus = con.getResponseMessage();
System.out.println(responseStatus);
BufferedReader in = new BufferedReader(new InputStreamReader(
con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
     

// You can play with response which is available as string now:
String finalvalue= response.toString();
     

// or you can parse/substring the required tag from response as below based your response code
finalvalue= finalvalue.substring(finalvalue.indexOf("<response>")+10,finalvalue.indexOf("</response>")); */
     

return finalvalue;
}
catch (Exception e) {
return e.getMessage();
}
}

}