webservice筆記--51學院webservice教程

xsd ---->定義了schema標籤的namespace
wsdl ---->定義了wsdl標籤的namespace


<definitions>


<types>
<schema>
<element>
</schema>
</types>
<message>
</message>
<portType>
</portType>

<binding>
</binding>
<services>
<port>
</port>
</services>
</definitions>
















課程安排:
web service 2
ibatis (mybatis) 1


複習2個相關的重要知識
a schema約束
1. namespace : schema文件的標識屬性,相當於id,每個schema文件需要有一個唯一的namespace值
2. targetNameSpace :指定當前schema文件的namespace值
3. xmlns :引入一個schema約束,它的值爲一個schema的namespace值
4. element :定義標籤
b. Http協議
1. 請求的組成:
請求行,請求頭,請求體(Post)
client--》server
2. 響應的組成
狀態行,響應頭,響應體
server-->client
3. 請求的過程

webservice
http+xml(schema)

Web service是什麼?
web服務:服務器端整出一些資源可以讓客戶端應用訪問(獲取數據)
一個跨語言、跨平臺的規範(抽象)
多個跨平臺、跨語言的應用間通信整合的方案(實際)

爲什麼要用Web service?
業務需求:
應用A: java寫的,運行在windows平臺下
List<User> getAllUsers();
應用B: c語言寫的,運行在linux平臺下
需要請求應用A的getAllUsers()得到所有的user信息來展示
web service能解決:
跨平臺調用 
跨語言調用
遠程調用

什麼時候使用web Service?
同一家公司的新舊應用
不同公司的應用間
分析業務需求:淘寶網與順風物流系統如何交互?
一些提供數據的內容聚合應用:天氣預報、股票行情

如何做web service的開發?
服務器端(處理客戶端應用的請求,執行業務邏輯,提供數據)
客戶端(發送請求,獲取數據)

幾個常用的
WSDL:web service definition language
對應一種類型的文件.wsdl
一個web service對應一個唯一的wsdl文檔
定義了web service的服務器端與客戶端應用交互傳遞請求和響應數據的格式和方式
SOAP:simple object access protocal
http+xml片斷
soap消息:請求消息和響應消息
它依賴於wsdl文檔的定義
SEI:Service EndPoint Interface
web service的終端接口,就是服務器端用來處理請求的接口(其中的方法就是處理請求的方法)
CXF:Celtix and XFire
一個apache的webservice框架

開發web service
1. 使用JDK開發
①. 服務器端
編碼:
a. 創建一個基於jdk6以上版本的java工程
b. 定義SEI web service Endpoint interface(web service終端接口)
@WebService
public interface HelloWS {
@WebMethod
public String sayHello(String name);
}
c. 定義SEI的實現類:
@WebService
public class HelloWSImpl implements HelloWS {
@Override
public String sayHello(String name) {
System.out.println("sayHello "+name);
return "hello "+name;
}
}


發佈:
public class Server {
public static void main(String[] args) {
//客戶端發送web service請求的url
String address = "http://192.168.1.104/ws_server/helloWS";
//處理請求的SEI對象
HelloWS helloWS = new HelloWSImpl();
//發佈web service
Endpoint.publish(address, helloWS);
System.out.println("發佈web service成功!");
}
}
②. 客戶端
1. eclipse Web Service瀏覽器
a. 查看Web Service所對應的WSDL文檔:...?wsdl
b. 使用eclipse訪問
請求體:SOAP Request Envelope
<soapenv:Envelope>
<soapenv:Body>
<q0:sayHello>
<arg0>tt</arg0>
</q0:sayHello>
</soapenv:Body>
</soapenv:Envelope>
響應體:SOAP Response Envelope
<S:Envelope>
<S:Body>
<ns2:sayHelloResponse xmlns:ns2="http://ws.java.atguigu.net/">
<return>hello tt</return> 
</ns2:sayHelloResponse>
</S:Body>
</S:Envelope>
2. 編碼實現
a. 創建客戶端java應用
b. 在應用的src下執行cxf的命令生成客戶端代碼:
wsimport -keep http://192.168.1.104/ws_server/helloWS?wsdl
c. 編寫客戶端調用的測試代碼,執行:
public class Client {
public static void main(String[] args) {
//創建SEI的工廠對象
HelloWSImplService factory = new HelloWSImplService();
//得到一個SEI實現類對象
HelloWSImpl helloWS = factory.getHelloWSImplPort();
//調用SEI的方法,此時纔去發送web Service請求,並得到返回結果
String result = helloWS.sayHello("Tom");
System.out.println(result);
}
}

③.請求過程記錄:使用eclipse的tcp/ip工具進行請求的監控
1. 配置tcp/ip監視器(請求轉發+請求信息記錄)
監聽port : 80(wsdl文件中的address屬性一致)
監聽主機 :ip
轉發的port : 8989(server端一致)
2. 將webservice的wsdl文件保存到client應用中: helloWS.wsdl
3. 修改helloWS.wsdl文件中的uri : port adresss屬性 8989-->80
4. 在client應用的src下執行命令: wsimport -keep 本地wsdl文件---》生成client端代碼
5. 藉助生成的代碼請求web service

④.應用:編寫天氣預報web service的客戶端(根據城市名稱得到城市今天的天氣信息)
1. 找到對應的wsdl文件所在的網絡地址:http://webservice.webxml.com.cn/WebServices/WeatherWebService.asmx?wsdl
2. 通過eclipse的web service瀏覽請求,測試是否可用
3. 將wsdl文檔保存到本地:ws_weath應用文件夾下weath.wsdl
(將<s:element ref="s:schema" /><s:any /> 替換成 <s:any minOccurs="2" maxOccurs="2"/>)
4. ws_weath應用下執行命令:wsimport -keep weath.wsdl的路徑  ---》生成對應的客戶端代碼
5. 編寫測試程序:
public static void main(String[] args) {
WeatherWebService factory = new WeatherWebService();
WeatherWebServiceSoap webServiceSoap = factory.getWeatherWebServiceSoap();
ArrayOfString arrayOfString = webServiceSoap.getWeatherbyCityName("荊州");
List<String> list = arrayOfString.getString();
for(String s : list) {
System.out.println(s);
}
}


2. 使用CXF框架開發

①.CXF : xfire-->xfire + celtrix
做web service開發的開源框架

②.開發Server端:
加入cxf的Jar包即可,其它不需要動

③.wsdl文件分析 :
<?xml version='1.0' encoding='UTF-8'?>
<!-- 
wsdl的作用:定義了web service的服務器端與客戶端應用交互傳遞請求和響應數據的格式和方式
請求的url
-->
<wsdl:definitions 
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
xmlns:tns="http://ws.java.atguigu.net/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
name="HelloWSImplService" 
targetNamespace="http://ws.java.atguigu.net/">
<!-- 
定義當前wsdl文檔中所使用的標籤,很重要的一部分爲soap消息中xml片斷所包含的一些標籤
<sayHello>
<arg0>文本</arg0>
</sayHello>
===>soap請求的請求體的一部分
<sayHelloResponse>
<return></return>
</sayHelloResponse>
===>soap響應的請求體的一部分
-->
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://ws.java.atguigu.net/" 
elementFormDefault="unqualified"
targetNamespace="http://ws.java.atguigu.net/" version="1.0">

<xs:element name="sayHello" type="tns:sayHello" />
<xs:element name="sayHelloResponse" type="tns:sayHelloResponse" />

<xs:complexType name="sayHello">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="sayHelloResponse">
<xs:sequence>
<xs:element minOccurs="0" name="return" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>

<!-- 
定義請求消息,它的個數爲SEI方法個數的2倍
message name 指定消息的名稱
part element 指定消息的組成,依賴<types>中定義的某個element 
-->
<wsdl:message name="sayHelloResponse">
<wsdl:part element="tns:sayHelloResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="sayHello">
<wsdl:part element="tns:sayHello" name="parameters">
</wsdl:part>
</wsdl:message>

<!-- 
定義SEI接口
portType name 指定SEI接口名
operation 接口的操作,也就是其方法
input message 服務器端接收的消息,依賴指定的<message>,也就是將哪個消息作爲請求消息對應方法的參數
output message 指定服務器返回的消息,依賴指定的<message>,也就是將哪個消息作爲響應消息對應方法的返回值
-->
<wsdl:portType name="HelloWS">
<wsdl:operation name="sayHello">
<wsdl:input message="tns:sayHello" name="sayHello">
</wsdl:input>
<wsdl:output message="tns:sayHelloResponse" name="sayHelloResponse">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<!-- 
定義服務器端處理請求的SEI實現類對象
binding type :參照<portType>所定義的SEI
soap:binding : 綁定的方式,也就數據傳遞的方式 爲文檔(即xml)
input : 指定請求消息(與<portType>中的input對應)
output:指定響應消息(與<portType>中的output對應)
body use="literal" 消息體爲文本
-->
<wsdl:binding name="HelloWSImplServiceSoapBinding" type="tns:HelloWS">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="sayHello">
<soap:operation soapAction="" style="document" />
<wsdl:input name="sayHello">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="sayHelloResponse">
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<!-- 定義客戶端調用web service的入口
service name :生產客戶端的SEI接口實現的工廠類
port binding :處理請求的服務器端的SEI接口實現類對象
address location :web service的請求url
-->
<wsdl:service name="HelloWSImplService">
<wsdl:port binding="tns:HelloWSImplServiceSoapBinding" name="HelloWSImplPort">
<soap:address location="http://192.168.1.104/ws_server/helloWS" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

④.web service 處理複雜數據
1. 數組集合(List,Set,Map)
2. 自定義類型

⑤. 一次web service請求的流程(以請求getStudentById(int id)它爲例)

1. 客戶端調用:helloWS.getStudentById(1),根據wsdl文檔生成一個xml片斷
<?xml version="1.0"  standalone="no"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:getStudentById xmlns:ns2="http://server.ws.java.atguigu.net/">
<arg0>1</arg0>
</ns2:getStudentById>
</S:Body>
</S:Envelope>
2. 將上面的xml數據作爲請求體,向服務器發送一個http請求(即soap消息, in消息)
3. 服務器端接收到這個請求後,取出請求體的內容
4. 服務器解析請求體,得到數據,並調用對應的web service對象的方法(依據wsdl文檔)
5. 將方法執行的返回值,根據wsdl文檔生成一個新的xml片斷:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:getStudentByIdResponse xmlns:ns2="http://server.ws.java.atguigu.net/">
<return>
<age>12</age>
<id>1</id>
<name>Jack</name>
</return>
</ns2:getStudentByIdResponse>
</soap:Body>
</soap:Envelope>
6. 將新xml片斷作爲響應體,返回給客戶端(soap消息, out消息)
7. 客戶端接收到響應數據,解析其響應體中的xml片斷,封裝成student對象
8. 將生成的student對象作爲helloWS.getStudentById(1)的返回值,整個請求結束


3 攔截器
爲了讓程序員能訪問或修改soap消息中的內容,cxf設計的攔截器
①系統攔截器
服務器端攔截器:
//通過終端類將helloWS對象發佈到address上
EndpointImpl endpoint = (EndpointImpl) Endpoint.publish(address, helloWS);
//在服務器端添加一個日誌的in攔截器
endpoint.getInInterceptors().add(new LoggingInInterceptor());
//在服務器端添加一個日誌的out攔截器
endpoint.getOutInterceptors().add(new LoggingOutInterceptor());
客戶端攔截器:
//創建生成SEI實現對象的工廠
HelloWSImplService factory = new HelloWSImplService();
//得到web service的SEI的實現類對象(動態生成的對象)
HelloWS helloWS = factory.getHelloWSImplPort();
//得到客戶端對象
Client client = ClientProxy.getClient(helloWS);
//添加客戶端的out攔截器
client.getOutInterceptors().add(new LoggingOutInterceptor());
//添加客戶端的in攔截器
client.getInInterceptors().add(new LoggingInInterceptor());
Student s = helloWS.getStudentById(1);
System.out.println("返回 "+s.getName());
②自定義攔截器
AbstractPhaseInterceptor:抽象過程攔截器,一般自定義的攔截器都會繼承於它
功能:通過自定義攔截器實現用戶名和密碼的檢查
1. 服務器端:
設置in攔截器,從soap消息中獲取用戶名和密碼數據,如果不滿足條件就不執行web service的方法
public class MyIntercept extends AbstractPhaseInterceptor<SoapMessage> {
public MyIntercept() {
super(Phase.PRE_PROTOCOL);
}
@Override
public void handleMessage(SoapMessage msg) throws Fault {
System.out.println("-----handleMessage");

Header header = msg.getHeader(new QName("atguigu"));
if(header==null) {
throw new Fault(new RuntimeException("用戶名密碼不存在 "));
} else {
Element data = (Element) header.getObject();
if(data==null) {
throw new Fault(new RuntimeException("用戶名密碼不存在22"));
} else {
String name = data.getElementsByTagName("name").item(0).getTextContent();
String password = data.getElementsByTagName("password").item(0).getTextContent();
if(!"xfzhang".equals(name) || !"amomo163".equals(password)) {
throw new Fault(new RuntimeException("用戶名密碼不正確"));
}
}
}
System.out.println("通過攔截器了!");
}
}
2. 客戶端:
設置out攔截器,向soap消息中添加用戶名和密碼數據
public class MyIntercept extends AbstractPhaseInterceptor<SoapMessage> {
private String name;
private String password;


public MyIntercept(String name, String pasword) {
super(Phase.PRE_PROTOCOL);
this.name = name;
this.password = pasword;


}


@Override
public void handleMessage(SoapMessage msg) throws Fault {
System.out.println("-----handleMessage");
Document document = DOMUtils.createDocument();
//<atguigu>
Element atguiguEle = document.createElement("atguigu");
//<name>xfzhang_amomo163</name>
Element nameEle = document.createElement("name");
nameEle.setTextContent(name);

//<password>xfzhang_amomo163</password>
Element passwordEle = document.createElement("password");
passwordEle.setTextContent(password);

atguiguEle.appendChild(nameEle);
atguiguEle.appendChild(passwordEle);

//添加爲請求消息的<soap:Header>
msg.getHeaders().add(new Header(new QName("atguigu"), atguiguEle));
}


}



3. 說明:用戶名和密碼是以xml片斷的形式存放在soap消息中的
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<atguigu>
<name>xfzhang</name>
<password>amomo163</password>
</atguigu>
</soap:Header>
<soap:Body>
<ns2:getStudentById xmlns:ns2="http://server.ws.java.atguigu.net/">
<arg0>1</arg0>
</ns2:getStudentById>
</soap:Body>
</soap:Envelope>

④用cxf做基於spring的web service開發
server端:
1. 新建web工程,導入cxf的jar包
2. 定義SEI和其實現
@WebService
public interface OrderService {


@WebMethod
public Order getOrderByNo(String orderNo);
}

@WebService
public class OrderServiceImpl implements OrderService {


public OrderServiceImpl() {
System.out.println("OrderServiceImpl()");
}


@Override
public Order getOrderByNo(String orderNo) {
System.out.println("getOrderByNo() orderNO=" + orderNo);
return new Order(3, orderNo, 10000000);
}


}

public class Order {


private int id;
private String orderNo;
private double price;
}
3. 發佈web service
beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans  
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<!-- 
配置一些cxf的核心bean
-->
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

<!-- 
終端:發佈webservice(將SEI的實現類對象與web service所提供的網絡地址關聯)
-->
<jaxws:endpoint id="orderService" implementor="net.atguigu.java.ws_spring.ws.OrderServiceImpl"
address="/orderService" />


</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
 <display-name>day101_ws_spring</display-name>
 <welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
 </welcome-file-list>
 
 <!-- 配置上下文初始化參數:指定cxf的spring的beans.xml -->
 <context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>classpath:beans.xml</param-value>
  </context-param>
  
  <!-- 加載上面參數的配置文件的listener -->
  <listener>
 <listener-class>
org.springframework.web.context.ContextLoaderListener
 </listener-class>
  </listener>
  
  <!-- 匹配所有請求,將請求先交給cxf框架處理 -->
  <servlet>
 <servlet-name>CXFServlet</servlet-name>
 <servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
 </servlet-class>
 <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
 <servlet-name>CXFServlet</servlet-name>
 <url-pattern>/*</url-pattern>
  </servlet-mapping>
  
</web-app>

client端
1. 新建web工程,導入cxf的jar包
2. 根據wsdl文檔生成客戶端代碼(src下)
3. client-beans.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans  
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<!--
配置發送請求的客戶端
-->
<jaxws:client id="orderClient" serviceClass="net.atguigu.java.ws_spring.ws.OrderService"
address="http://localhost:8080/day101_ws_spring/orderService" />
</beans>
4. 編寫測試代碼:
public static void main(String[] args) {
//加載client-beans.xml
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"client-beans.xml");
//根據bean的id加載所對應的OrderService
OrderService orderService = (OrderService) context.getBean("orderClient");
//調用方法,發送soap請求
Order order = orderService.getOrderByNo("hao123");
System.out.println("client " + order.getId() + "_" + order.getOrderNo()
+ "_" + order.getPrice());
}
5. 添加攔截器
客戶端:
<jaxws:client id="orderClient" serviceClass="com.atguigu.day01_ws_server.ws.OrderWs"
address="http://localhost:8989/day02_cxf_spring_server/orderws">
<jaxws:outInterceptors>
<bean class="com.atguigu.day01_ws_server.ws.intercepter.AddUserIntercepter">
<property name="name" value="xfzhang"></property>
<property name="pwd" value="123456"></property>
</bean>
</jaxws:outInterceptors>
</jaxws:client>
服務器端:
<jaxws:endpoint id="orderWs"
implementor="com.atguigu.day01_ws_server.ws.OrderWsImpl" address="/orderws">
<jaxws:inInterceptors>
<bean class="com.atguigu.day01_ws_server.intercepter.CheckUserIntercepter"/>
</jaxws:inInterceptors>
</jaxws:endpoint>
補充:
1. 用ajax訪問webservice
a. 使用原生的ajax : ajax.jsp
b. 使用jquery的ajax : ajax2.jsp


2. 用HttpURLConnection訪問web service : HttpServletConnectionTest

Web Service好比一個服務供應商,給其他廠家提供基礎服務,其他廠家再將這個服務包裝成自己的產品或者服務提供給別人或自己使用。既然兩個公司需要合作,不可能靠一句話就可以的,就需要一些標準和規範的東西來實現。那麼:


SOAP 就像兩個公司之間籤的合同,約束雙方按一定規矩和標準辦事。


WSDL 則像說明書,告訴別人你有什麼,能給別人提供什麼服務。










發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章