第四課:RESTful Webservice 編程

課程網站:

相關文章:

Restful 服務端編程


Restful簡介

RPC(Remote Procedure Call Protocol)溪食,即遠程過程調用僧诚,是一種進程之間通信的技術萍鲸。在一開始厢破,電腦的各個進程之間是無法通訊的景醇。為了讓進程之間能夠互相通訊微饥,于是就發(fā)明了IPC(Inter-process communication颅眶,單機中運行的進程之間的相互通信)技術都哭。隨著互聯(lián)網的出現(xiàn)啊易,人們又將IPC擴展成為了RPC吁伺,于是實現(xiàn)了不同機器之間進程的通訊。

但是租谈,實現(xiàn)RPC是很麻煩的篮奄。為了讓RPC變得簡單,程序員發(fā)明了各種各樣的框架割去。Restful就是其中一類高效以及簡潔易用的架構風格窟却。

REST是一種架構風格,其核心是面向資源呻逆,REST專門針對網絡應用設計和開發(fā)方式夸赫,以降低開發(fā)的復雜性,提高系統(tǒng)的可伸縮性咖城。REST提出設計概念和準則為:
1. 網絡上的所有事物都可以被抽象為資源(resource)
2. 每一個資源都有唯一的資源標識(resource identifier)茬腿,對資源的操作不會改變這些標識
3. 所有的操作都是無狀態(tài)的
REST簡化開發(fā)呼奢,其架構遵循CRUD原則,該原則告訴我們對于資源(包括網絡資源)只需要四種行為:創(chuàng)建切平,獲取握础,更新和刪除就可以完成相關的操作和處理。您可以通過統(tǒng)一資源標識符(Universal Resource Identifier悴品,URI)來識別和定位資源禀综,并且針對這些資源而執(zhí)行的操作是通過 HTTP 規(guī)范定義的。其核心操作只有GET,PUT,POST,DELETE苔严。
由于REST強制所有的操作都必須是stateless的定枷,這就沒有上下文的約束,如果做分布式邦蜜,集群都不需要考慮上下文和會話保持的問題依鸥。極大的提高系統(tǒng)的可伸縮性。
對于SOAP Webservice和Restful Webservice的選擇問題悼沈,首先需要理解就是SOAP偏向于面向活動贱迟,有嚴格的規(guī)范和標準,包括安全絮供,事務等各個方面的內容衣吠,同時SOAP強調操作方法和操作對象的分離,有WSDL文件規(guī)范和XSD文件分別對其定義壤靶。而REST強調面向資源缚俏,只要我們要操作的對象可以抽象為資源即可以使用REST架構風格。

參考資料:
http://blog.csdn.net/lilongsheng1125/article/details/41643665
https://www.zhihu.com/question/25536695

CXF Restful Service 基礎

在下載程序包贮乳,例如最新的apache-cxf-3.1.7-src.zip忧换。然后解壓到自己喜歡的文件夾。例如我是放在了桌面向拆。

apache-cxf-3.1.7-src

這里面的文件特別多亚茬。我們要找的是一個sample。我們打開神奇的IntelliJ IDEA浓恳,然后import project刹缝。找到apache-cxf-3.1.7-src\distribution\src\main\release\samples\jax_rs\basic這個路徑。然后雙擊pom.xml颈将。然后一直按next就可以打開這個項目了梢夯。

打開項目

然后和上次一樣,在右邊找到框晴圾,輸入命令:

  • install:安裝颂砸,你會看到制品放入本地倉庫
  • -Pserver: 運行服務器,程序會自動啟動 jetty
  • -Pclient: 運行客戶端程序

我們只需要分別運行 install 和 -Pserver。 然后沾凄,在瀏覽器中輸入 URL:http://localhost:9000/customerservice/customers/123 梗醇。我們就看到了運行的結果。

運行結果

返回的是萬能的xml撒蟀。于是簡單的程序體驗就到這里了。

CXF 與 spring 集成

maven創(chuàng)建web項目:選擇maven-archetype-webapp温鸽,然后輸入:

groupId: me.cxf-sample
artifactId: cxf-rs-spring

項目名為cxf-rs-spring保屯。

然后我們創(chuàng)建在main文件夾里面創(chuàng)建java文件夾,然后把它設置為Source Root涤垫。接著加入Java包姑尺,命名為demo.jaxrs.server。

加入Java包

然后我們將剛剛Demo中的Customer.java, CustomerService.java, Product.java, Order.java拖入到該包蝠猬。

這個時候切蟋,發(fā)現(xiàn)沒有rs包。

沒有rs包

我們添加依賴:

<dependency>
    <groupId>javax.ws.rs</groupId>
    <artifactId>javax.ws.rs-api</artifactId>
    <version>2.0.1</version>
</dependency>

按系統(tǒng)文檔配置src/main/webapp/WEB-INF/web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>cxf-rs-spring Maven Webapp</display-name>

  <!-- 設置Spring容器加載配置文件路徑 -->
  <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/beans.xml</param-value>
  </context-param>
  <!-- 加載Spring容器配置 -->
  <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <listener>
        <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
  </listener>

  <!-- 加載CXFServlet容器配置 -->
  <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
  </servlet>
  <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/*</url-pattern>
  </servlet-mapping>

</web-app>

再在同一文件夾下榆芦,創(chuàng)建beans.xml柄粹,然后繼續(xù)配置

創(chuàng)建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:jaxrs="http://cxf.apache.org/jaxrs"
  xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://cxf.apache.org/jaxrs
        http://cxf.apache.org/schemas/jaxrs.xsd">

  <!-- do not use import statements if CXFServlet init parameters link to this beans.xml -->

  <import resource="classpath:META-INF/cxf/cxf.xml" />
  <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

  <jaxrs:server id="customerService" address="/service1">
        <jaxrs:serviceBeans>
          <ref bean="customerBean" />
        </jaxrs:serviceBeans>
  </jaxrs:server>

  <bean id="customerBean" class="demo.jaxrs.server.CustomerService" />
</beans>

最后,我們配置pom.xml匆绣。然后導入需要的包驻右。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>me.cxf-sample</groupId>
  <artifactId>cxf-rs-spring</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>cxf-rs-spring Maven Webapp</name>
  <url>http://maven.apache.org</url>

  <!-- 申明常用軟件的版本 -->
  <properties>
    <jettyVersion>9.3.7.v20160115</jettyVersion>
    <cxf.version>3.1.6</cxf.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>javax.ws.rs</groupId>
      <artifactId>javax.ws.rs-api</artifactId>
      <version>2.0.1</version>
    </dependency>
    <!-- CXF jaxrs 依賴 -->
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-transports-http</artifactId>
      <version>${cxf.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-frontend-jaxrs</artifactId>
      <version>${cxf.version}</version>
    </dependency>

    <!-- CXF jaxrs description 依賴 -->
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-rs-service-description</artifactId>
      <version>${cxf.version}</version>
    </dependency>

    <!-- spring 依賴 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>3.2.8.RELEASE</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>cxf-rs-spring</finalName>
    <plugins>
      <plugin>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>${jettyVersion}</version>
      </plugin>
    </plugins>
  </build>
</project>

最后用jetty:run啟動服務器。然后崎淳,在瀏覽器輸入:http://localhost:8080/http://localhost:8080/service1/customerservice/customers/123 看效果就行了堪夭。

如果我們輸入http://localhost:8080/service1?_wadl 就可以看到該服務接口的定義。

我做的時候拣凹,出現(xiàn)了下面這個問題森爽,服務器啟動不了。找了很久嚣镜,原來是之前搞apache的時候爬迟,服務器沒有關閉。于是關閉服務器就好了祈惶。

[INFO] Jetty server exiting.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12.110s
[INFO] Finished at: Tue Sep 27 22:13:51 CST 2016
[INFO] Final Memory: 25M/249M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.eclipse.jetty:jetty-maven-plugin:9.3.7.v20160115:run (default-cli) on project cxf-rs-spring: Failure: Address already in use: bind -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

JAX-RS 服務端編程

將cxf-rs-spring文件夾復制出來雕旨,改名為cxf-rs-spring-test。然后進入pom.xml捧请,將原來<artifactId>cxf-rs-spring</artifactId>改為<artifactId>cxf-rs-spring-test</artifactId>凡涩。

然后我們用IntelliJ IDEA打開這個文件。在java文件夾下疹蛉,創(chuàng)建org.me.examples.helloworld活箕,然后在這個包下面新建Hello.java。

使用下面代碼:

package org.me.examples.helloworld;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

@Path("/hello/{username}")
public class Hello {
    @GET
    @Produces("text/xml")
    public String sayHello(@PathParam("username") String userName) {
        return "hello " + userName;
    }
}

再將beans.xml文件下的關鍵部分稍作修改可款。

<jaxrs:server id="customerService" address="/service1">
        <jaxrs:serviceBeans>
            <ref bean="customerBean" />
            <ref bean="Hello" />
        </jaxrs:serviceBeans>
    </jaxrs:server>

    <bean id="customerBean" class="demo.jaxrs.server.CustomerService" />
    <bean id="Hello" class="org.me.examples.helloworld.Hello" />

然后我們使用命令行育韩,輸入命令:curl http://localhost:8080/service1/hello/Monkey克蚂。

效果如下:

效果圖

返回了“hello Monkey”,說明成功了筋讨。

習題部分


RESTful Webservice 作為遠程調用(RPC)埃叭,它的輸入是什么?輸入是什么悉罕?

REST核心是URL和面向資源赤屋,用URL代替了原來復雜的操作方法,把它簡化成了GET,PUT,POST,DELETE四個核心操作壁袄。一個URL對應著一個資源类早。因此我們可以認為,輸入是一段URL嗜逻,輸出是一段資源(例如Json或者xml)涩僻。

有一個 Java 遠程函數(shù)聲明 public Customer getCustomer(String id),把 HTTP 協(xié)議輸入/輸出映射到該函數(shù)栈顷,至少要申明(annotation)哪些信息逆日?請寫出這些 annotation,并簡單解釋協(xié)議與函數(shù)的關系妨蛹。

@GET
@Path("/customers/{id}/")

修改 實驗1 的 @Path 為 @Path(“/myservice/”)屏富,請用 curl -v 給出結果

我們將@Path("/customerservice/")改為@Path(“/myservice/”)。我們知道蛙卤,@Path("/customerservice/")表示項目根訪問該類的URI路徑狠半。如果我們將其換成myservice,那么URL也要換成http://localhost:9000/myservice/customers/123颤难。同理神年,如果我們換的是@Path("/customers/…")部分,對應的URL也要改變行嗤。否則就會顯示404已日。實驗結果如下:

實驗結果

簡述 GET 與 POST 的區(qū)別

Http獲取資源的核心操作有四個:GET,POST栅屏,PUT飘千,DELETE,對應著對這個資源的查栈雳,改护奈,增,刪4個操作哥纫。因此原理上霉旗,GET方法得到資源是不能修改信息的,而POST代表資源有可能會被修改。

在表面上厌秒,GET和POST的區(qū)別表現(xiàn)在以下方面:

  1. GET請求的數(shù)據會附在URL之后(就是把數(shù)據放置在HTTP協(xié)議頭中)读拆,POST把提交的數(shù)據則放置在是HTTP包的包體中。
  2. GET方式提交的數(shù)據最多只能是1024字節(jié)(因為GET用URL提交數(shù)據鸵闪,所有關鍵在于瀏覽器對URL的支持)檐晕,理論上POST沒有限制,可傳較大量的數(shù)據(實際為了安全起見蚌讼,也有對應的限制)棉姐。
  3. 在用不同技術編程的時候,GET和POST的使用方法不一樣啦逆。
  4. POST的安全性要比GET的安全性高。比如:通過GET提交數(shù)據笛洛,用戶名和密碼將明文出現(xiàn)在URL上夏志,因為(1)登錄頁面有可能被瀏覽器緩存,(2)其他人查看瀏覽器的歷史紀錄苛让,那么別人就可以拿到你的賬號和密碼了沟蔑,除此之外,使用GET提交數(shù)據還可能會造成Cross-site request forgery攻擊狱杰。

參考資料:
http://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html

在實際應用中 Map<Long, Customer> customers 使用會產生副作用嗎瘦材?為什么?

在多線程編程的時候仿畸,會產生副作用食棕。有可能兩個用戶同時請求同一個ID。因此要盡量避免這種情況错沽。

Spring IOC 與 DI 是 java 編程核心內容之一簿晓。閱讀Spring IoC 使用詳解用自己的語言解釋 IoC 解耦原理。

IoC通過依賴注入千埃,使用set或者構造方法實現(xiàn)了解耦憔儿。就是本來要修改一個接口的實現(xiàn),必須要修改里面的內容放可,用依賴注入谒臼,就只需要set一下,就可以了耀里。

(r阽汀)使用 Maven 構建(6)給出的案例,項目名稱 IOC_test

……

在 Spring 試驗中备韧, 閱讀 beans.xml劫樟, 寫出申明根資源的關鍵內容

<jaxrs:server id="customerService" address="/service1">
    <jaxrs:serviceBeans>
        <ref bean="customerBean" />
    </jaxrs:serviceBeans>
</jaxrs:server>

<bean id="customerBean" class="demo.jaxrs.server.CustomerService" />

(!)嘗試寫一個根資源類 Hello,用 GET 方法實現(xiàn) String SayHello() 遠程服務叠艳。請給出 Hello類代碼奶陈,增加根資源的配置(不是修改)XML內容,curl測試結果附较。

package org.me.examples.helloworld;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

@Path("/hello/{username}")
public class Hello {
    @GET
    @Produces("text/xml")
    public String sayHello(@PathParam("username") String userName) {
        return "hello " + userName;
    }
}
<jaxrs:server id="customerService" address="/service1">
        <jaxrs:serviceBeans>
            <ref bean="customerBean" />
            <ref bean="Hello" />
        </jaxrs:serviceBeans>
    </jaxrs:server>

    <bean id="customerBean" class="demo.jaxrs.server.CustomerService" />
    <bean id="Hello" class="org.me.examples.helloworld.Hello" />

WADL 的全稱吃粒。從程序結果返回中截取一段 WADL 內容。

WADL全稱為Web Application Description Language拒课,即Web 應用程序描述語言徐勃。

實驗返回的WADL

輸出對象格式協(xié)商是Resquest的Header段的哪個 Field 決定的?

Accept:application/xml

JAX-RS 的全稱

JAX-RS即Java API for RESTful Web Services早像,是一個Java 編程語言的應用程序接口僻肖,支持按照表述性狀態(tài)轉移(REST)架構風格創(chuàng)建Web服務。

@Produces 和 @Consumes 的作用

@Produces:
用于指定輸出格式卢鹦,參數(shù)可以是字符串數(shù)組臀脏。 如果是一個數(shù)組,第一個是默認輸出格式冀自。如果客戶端指定輸出揉稚,則會匹配其他輸出。 例如:

@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})

@Consumes:
指定處理輸入媒體的類型熬粗,例如 “Content-Type: application/json”搀玖。 如果你的服務中函數(shù)中帶一個自由的參數(shù),規(guī)定用合適的 輸入 provider 驻呐,使用 body 中內容灌诅,實例化這個參數(shù)。例如:

@POST
@Consumes("text/plain")
@Produces("text/xml")
public String sayHello(@PathParam("username") String userName, String letter) {
        return "hello " + userName + ":" + letter;
}

(1┦稀)請完成 實驗3 的每個參數(shù)處理的內容延塑,然后在jesery上完成類似工作。請描述兩個平臺是否完全兼容答渔?簡述不兼容的內容关带。

……

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市沼撕,隨后出現(xiàn)的幾起案子宋雏,更是在濱河造成了極大的恐慌,老刑警劉巖务豺,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件磨总,死亡現(xiàn)場離奇詭異,居然都是意外死亡笼沥,警方通過查閱死者的電腦和手機蚪燕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門娶牌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人馆纳,你說我怎么就攤上這事诗良。” “怎么了鲁驶?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵鉴裹,是天一觀的道長。 經常有香客問我钥弯,道長径荔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任脆霎,我火速辦了婚禮总处,結果婚禮上,老公的妹妹穿的比我還像新娘睛蛛。我一直安慰自己辨泳,他們只是感情好,可當我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布玖院。 她就那樣靜靜地躺著,像睡著了一般第岖。 火紅的嫁衣襯著肌膚如雪难菌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天蔑滓,我揣著相機與錄音郊酒,去河邊找鬼。 笑死键袱,一個胖子當著我的面吹牛燎窘,可吹牛的內容都是我干的。 我是一名探鬼主播蹄咖,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼褐健,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了澜汤?” 一聲冷哼從身側響起蚜迅,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎俊抵,沒想到半個月后谁不,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡徽诲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年刹帕,在試婚紗的時候發(fā)現(xiàn)自己被綠了吵血。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡偷溺,死狀恐怖蹋辅,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情亡蓉,我是刑警寧澤晕翠,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站砍濒,受9級特大地震影響淋肾,放射性物質發(fā)生泄漏。R本人自食惡果不足惜爸邢,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一樊卓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧杠河,春花似錦碌尔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至待诅,卻和暖如春叹坦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背卑雁。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工募书, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人测蹲。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓莹捡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親扣甲。 傳聞我的和親對象是個殘疾皇子篮赢,可洞房花燭夜當晚...
    茶點故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內容