java 使用WebService實現(xiàn)接口的發(fā)布和訪問(附python客戶端調(diào)用方式)

最近公司業(yè)務(wù)需要對接第三方的接口,這些接口用的是webService基于soap+xml發(fā)布的,這技術(shù)對于以前寫業(yè)務(wù)接口的前輩來說應(yīng)該是滾瓜爛熟的了。什么?你居然不熟悉在pojo和xml之間互相轉(zhuǎn)換?瑪莎拉蒂不會開????那你無了。在那個年代webservice算是炙手可熱的一門技術(shù)了,所以還有不少的老項目是基于webservice的,但是對于新生代的業(yè)務(wù)人員來說,這樣的技術(shù)如果無人提及,可能咱也不會想著去學習,即便后面webservice可以實現(xiàn)restful風格的接口,但現(xiàn)在springboot等這樣的框架是完全著力于restful規(guī)范的,非特殊業(yè)務(wù)不會用到webservice去開發(fā)。咱學習積極性高,權(quán)當了解,才不是為了業(yè)務(wù)需求。言歸正傳,一起來熟悉一下吧??

概要

'''
Web Service所使用的是Internet上統(tǒng)一、開放的標準,如HTTP、XML、SOAP(簡單對象訪問協(xié)議)、WSDL等,所以Web Service可以在任何支持這些標準的環(huán)境(Windows,Linux)中使用。
這有助于大量異構(gòu)程序和平臺之間的互操作性,從而使存在的應(yīng)用程序能夠被廣泛的用戶訪問。

從應(yīng)用來說,Web Service是一件值得企業(yè)特別注意的事情。Web Service的應(yīng)用范圍目前包括兩個方面:企業(yè)之間的應(yīng)用、以及企業(yè)內(nèi)部的應(yīng)用。

'''
WebService即web服務(wù),它是一種跨編程語言和跨操作系統(tǒng)平臺的遠程調(diào)用技術(shù)
JAVA 中共有三種WebService 規(guī)范,分別是JAX-WS(JAX-RPC)、JAXM&SAAJ、JAX-RS。
webService三要素:soap、wsdl、uddi

soap

即簡單對象訪問協(xié)議(Simple Object Access Protocol),它是用于交換XML(標準通用標記語言下的一個子集)編碼信息的輕量級協(xié)議,由Envelope,Headers,Body組成

wsdl

Web Service描述語言WSDL(SebService Definition Language)就是用機器能閱讀的方式提供的一個正式描述文檔而基于XML(標準通用標記語言下的一個子集)的語言,用于描述Web Service及其函數(shù)、參數(shù)和返回值。因為是基于XML的,所以WSDL既是機器可閱讀的,又是人可閱讀的。

uddi

用于服務(wù)注冊的,wsdl+uddi實現(xiàn)服務(wù)發(fā)現(xiàn)

如果一個功能,需要被多個系統(tǒng)使用可以使用webservice開發(fā)一個服務(wù)端接口,供不同的客戶端應(yīng)用。主要應(yīng)用在企業(yè)內(nèi)部系統(tǒng)之間的接口調(diào)用、面向公網(wǎng)的webservice服務(wù)。
咱只要弄清楚接口、契約、實現(xiàn)即可。
-定義接口并抽象出需要用到的功能方法
-創(chuàng)建實現(xiàn)類去實現(xiàn)該接口,并給出相應(yīng)的業(yè)務(wù)處理邏輯
下面給出一個供參考的webservice的服務(wù)端以及客戶端

服務(wù)端

首先定義業(yè)務(wù)接口

@WebService //標記該接口為webService接口
public interface WeatherServiceInterface {
    String getWeatherByCityName(String cityName);
}

實現(xiàn)該接口

public class WeatherServiceImpl implements WeatherServiceInterface {
    @Override
    public String getWeatherByCityName(String cityName) {
        return cityName + "天氣還可以!";
    }
}

發(fā)布服務(wù),需給定發(fā)布的地址 ip+port
1.使用javax擴展包下的Endpoint.publish發(fā)布服務(wù)

Endpoint.publish("http://localhost:8080/ws/weather", new WeatherServiceImpl());
System.out.println("Weather webservice  was published!");

2.使用Apache cxf發(fā)布服務(wù)

public class App {
    public static void main(String[] args) {
        JaxWsServerFactoryBean factoryBean = new JaxWsServerFactoryBean();

        factoryBean.setAddress("http://localhost:8080/ws/weather");

        factoryBean.setServiceBean(new WeatherServiceImpl());
        // 客戶端請求報文攔截器
        factoryBean.getInInterceptors().add(new LoggingInInterceptor());
        // 客戶端響應(yīng)報文攔截器
        factoryBean.getOutInterceptors().add(new LoggingOutInterceptor());

        Server server = factoryBean.create();
        server.start();

        System.out.println("Weather webservice   was published!");
    }
}

這里為了查看客戶端和服務(wù)端之間的溝通詳細過程,做了日志攔截打印,可以查看到請求頭和請求體以及響應(yīng)的相關(guān)內(nèi)容
發(fā)布成功后可以通過發(fā)布的地址后面加上 ?wsdl (說明書)查看服務(wù)接口業(yè)務(wù)方法的調(diào)用方式(細心看的同學肯定能找到接口名,接口方法,方法對應(yīng)的參數(shù)及類型等,這里就不貼出來了??)

客戶端

idea中新建module
客戶端需要把之前的WeatherServiceInterface拷貝過來,再利用CXF這個庫拿到這個接口代理實現(xiàn)類然后就可以調(diào)用接口中的方法了(具體如何實現(xiàn)有興趣可以去看看cxf這個庫的源碼)。

import static org.junit.Assert.assertTrue;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.junit.Test;

/**
 * Unit test for simple App.
 */
public class AppTest {
    /**
     * Rigorous Test :-)
     */
    @Test
    public void shouldAnswerWithTrue() throws Exception {
        JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
        jaxWsProxyFactoryBean.setAddress("http://localhost:8080/ws/weather");
        jaxWsProxyFactoryBean.setServiceClass(WeatherService.class);
        WeatherService weatherServiceProxy = (WeatherService)       
        jaxWsProxyFactoryBean.create();
        String weather = weatherServiceProxy.getWeatherByCityName("上海");
        System.out.println(weather);
    }
}

運行后,查看服務(wù)端的控制臺打印信息:

2020-12-20 16:50:06,369 544309 [ qtp20853837-18] INFO  plPort.WeatherServiceInterface  - Inbound Message
----------------------------
ID: 3
Address: http://localhost:8080/ws/weather
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml; charset=UTF-8
Headers: {Accept=[*/*], Cache-Control=[no-cache], connection=[keep-alive], Content-Length=[213], content-type=[text/xml; charset=UTF-8], Host=[localhost:8080], Pragma=[no-cache], SOAPAction=[""], User-Agent=[Apache CXF 3.0.1]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:getWeatherByCityName xmlns:ns2="http://example.org/">
<arg0>上海</arg0></ns2:getWeatherByCityName></soap:Body></soap:Envelope>
--------------------------------------
2020-12-20 16:50:06,373 544313 [ qtp20853837-18] INFO  plPort.WeatherServiceInterface  - Outbound Message
---------------------------
ID: 3
Response-Code: 200
Encoding: UTF-8
Content-Type: text/xml
Headers: {}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:getWeatherByCityNameResponse xmlns:ns2="http://example.org/"><return>上海天氣還可以!</return></ns2:getWeatherByCityNameResponse></soap:Body></soap:Envelope>
--------------------------------------

請求報文中的參數(shù)名稱(包括wsdl里面每個方法的參數(shù)名稱),這里顯示的是arg0,是可以通過接口參數(shù)注解@webParams("paramName")來指定的

在真正的業(yè)務(wù)中一個接口類里面可能會有很多業(yè)務(wù)方法,并且在沒有服務(wù)端接口類的情況下只看wsdl說明書,客戶端做相關(guān)業(yè)務(wù)處理效率太低了。

apache cxf 可以根據(jù)wsdl說明自動生成所有的業(yè)務(wù)類,使用wsdl2java命令即可生成一個業(yè)務(wù)包
(cxf下載地址http://cxf.apache.org/download.html

./wsdl2java -client http://localhost:8080/ws/weather?wsdl

windows下可以通過wsdl2java.bat命令生成
這里就不貼出來了

webService rest風格的例子:

server端

接口類

import org.example.pojo.User;

import java.util.List;

import javax.ws.rs.*;


@Path("/userService")
@Produces("*/*")
public interface IUserService {

    @POST
    @Path("/user")
    @Consumes({"application/xml", "application/json"})
    public void saveUser(@HeaderParam("auth") String auth, User user);

    @PUT
    @Path("/user")
    @Consumes({"application/xml", "application/json"})
    public void updateUser(User user);

    @GET
    @Path("/user")
    @Produces({"application/xml", "application/json"})
    public List<User> findAllUsers(@HeaderParam("auth") String auth);

    @GET
    @Path("/user/{id}")
    @Consumes("application/xml") // 服務(wù)器支持請求的數(shù)據(jù)類型
    @Produces({"application/xml", "application/json"}) //服務(wù)器支持的返回數(shù)據(jù)格式類型
    public User finUserById(@PathParam("id") Integer id);

    @DELETE
    @Path("/user/{id}")
    @Consumes({"application/xml", "application/json"})
    public void deleteUser(@PathParam("id") Integer id);
}

其中@Consumer指定請求的數(shù)據(jù)格式上面有支持xml和json的,@Produces返回數(shù)據(jù)格式(xml/json)
具體實現(xiàn)我這里只是一些簡單的處理和打印就不貼了。
發(fā)布服務(wù) 使用cxf中的工廠類


import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.example.service.UserServiceImpl;

/**
 * Create by zengqi on 2020/12/19
 **/
public class Server {
    public static void main(String[] args) {
        JAXRSServerFactoryBean jaxrsServerFactoryBean = new JAXRSServerFactoryBean();
        jaxrsServerFactoryBean.setAddress("http://localhost:8888/server/rest");
        jaxrsServerFactoryBean.setServiceClass(UserServiceImpl.class);
        jaxrsServerFactoryBean.create();
        System.out.println("Server  was published");
    }
}

如果你的實現(xiàn)類正常返回了數(shù)據(jù),這時候訪問http://localhost:8888/server/rest/userService/user可以拿到j(luò)son/xml數(shù)據(jù)了
接口地址拼接方式為:發(fā)布地址+接口Path+方法Path
這才是我們熟知的方式嘛。。。??

客戶端調(diào)用:

可以使用cxf里面的Client去請求,也可以使用網(wǎng)絡(luò)框架去請求,如HttpClient,okhttp,requests等,不贅述
使用cxf Client請求:

public class AppTest {
    /**
     * Rigorous Test :-)
     */
    @Test
    public void shouldAnswerWithTrue() {
        Collection<? extends User> collection = WebClient.create("http://localhost:8888/server/rest/userService/user")
                .accept(MediaType.APPLICATION_JSON)
                .getCollection(User.class);
        System.out.println(collection);
    }
}

MediaType可以指定返回格式為json/xml
到此,算是對webService有一個基本了解了!

這里記錄一下python調(diào)用webService的方法:
restful風格的對python來說可以通過一般的網(wǎng)絡(luò)請求庫直接調(diào)用,基于soap+xml的webService接口需要用到suds-jurko/zeep庫,下面簡單記錄一下調(diào)用方式

suds:

from suds.client import Client
from suds.xsd.doctor import ImportDoctor, Import

imp = Import('http://www.w3.org/2001/XMLSchema',
             location='http://www.w3.org/2001/XMLSchema.xsd')
imp.filter.add('http://WebXml.com.cn/')
doctor = ImportDoctor(imp)

imp = Import('http://www.w3.org/2001/XMLSchema',
             location='http://www.w3.org/2001/XMLSchema.xsd')
imp.filter.add('http://WebXml.com.cn/')
doctor = ImportDoctor(imp)
url = "http://localhost:8080/ws/weather?wsdl"
client = Client(url, doctor=doctor)
result = client.service.getWeatherbyCityName(u'廣州')
print(result)

zeep實現(xiàn)方式類似,但是這個庫據(jù)說比suds要更新(推薦)。

from zeep import Client

url = "http://localhost:8080/ws/weather?wsdl"
client = Client(url)
result = client.service.getWeatherbyCityName(u'廣州')
print(result)

值得注意的地方python調(diào)用的時候url后面需要跟上?wsdl
python也是能做webService接口的,定義接口,約束,以及實現(xiàn),便可以通過web庫發(fā)布服務(wù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容