亚洲日韩国产二区无码,亚洲AV无码成人专区,一区二区三区亚洲http://www.tkk7.com/shinzey/category/44814.html庸人不必自擾,智者何需千慮?zh-cnThu, 09 Feb 2012 11:33:13 GMTThu, 09 Feb 2012 11:33:13 GMT60JAX-RS 從傻逼到牛叉 7:注入參數的自動類型轉換http://www.tkk7.com/shinzey/archive/2012/01/10/368030.html蜀山兆孨龘蜀山兆孨龘Tue, 10 Jan 2012 05:17:00 GMThttp://www.tkk7.com/shinzey/archive/2012/01/10/368030.htmlhttp://www.tkk7.com/shinzey/comments/368030.htmlhttp://www.tkk7.com/shinzey/archive/2012/01/10/368030.html#Feedback2http://www.tkk7.com/shinzey/comments/commentRss/368030.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/368030.html

前面介紹了各種請求參數的注入,這些參數在 HTTP 請求中都是以純文本的方式存在的。在處理參數的時候,往往需要把這些文本參數轉換為 Java 對象。JAX-RS 提供了一些內置的規則里自動完成這種轉換。

轉換規則一覽

JAX-RS 提供了四條自動類型轉換規則,下面我們逐條考察。

原始類型

這個早就見識過了,無需多說。舉例回顧一下:

@GET
@Path("{id}")
public Movie getXxx(@PathParam("id") int id) {/*...*/}
    

提供接受單個 String 參數的構造器的類型

這個也不難理解,JAX-RS 會自動調用該構造器創建一個對象:

public class Style {
    public Style(String name) {/* ... */}
    // ...
}

@GET
@Path("{name}")
public Movie getXxx(@PathParam("name") Style style) {
    // JAX-RS 已自動調用 xxx = new Style(name)
    // ...
}
    

提供靜態工廠方法 valueOf(String) 的類型

也好理解。特別需要注意的是,所有的枚舉類型都在此列,因為編譯器會自動給枚舉類型加上一個這樣的工廠方法。例如:

public enum Style {/*...*/}

@GET
@Path("{name}")
public Movie getXxx(@PathParam("name") Style style) {
    // JAX-RS 已自動調用 style = Style.valueOf(name)
    // ...
}
    

類型參數滿足前兩個條件的 List<T>、Set<T>SortedSet<T>

這條規則適用于多值參數,例如查詢參數:

@GET
@Path("xxx")
public Movie getXxx(@QueryParam("style") Set<Style> styles) {
    // JAX-RS 已自動轉換每個 Style 對象并組裝到 Set 中
    // ...
}
    

轉換失敗的處理

如果轉換失敗,JAX-RS 會根據情況自動拋出一個包裝了初始異常,但是帶不同 HTTP 錯誤碼的 WebApplicationException:對矩陣參數(@MatrixParam)、查詢參數 (@QueryParam)或路徑參數(@PathParam)來說為 HTTP 404 找不到,而對頭部參數(@HeaderParam)或 Cookie 參數(@CookieParam)為 HTTP 400 錯誤請求。



蜀山兆孨龘 2012-01-10 13:17 發表評論
]]>
JAX-RS 從傻逼到牛叉 6:參數注入http://www.tkk7.com/shinzey/archive/2011/12/29/367499.html蜀山兆孨龘蜀山兆孨龘Thu, 29 Dec 2011 08:34:00 GMThttp://www.tkk7.com/shinzey/archive/2011/12/29/367499.htmlhttp://www.tkk7.com/shinzey/comments/367499.htmlhttp://www.tkk7.com/shinzey/archive/2011/12/29/367499.html#Feedback0http://www.tkk7.com/shinzey/comments/commentRss/367499.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/367499.html

《JAX-RS 從傻逼到牛叉 3:路徑匹配》中,我們已經見過如何使用 @PathParam、@QueryParam@MatrixParam 分別注入 URI 中的路徑參數、矩陣參數和查詢參數,以及如何編程訪問這些參數。本文介紹表單參數、HTTP 頭部參數和 Cookie 參數的注入。

表單參數

HTTP 請求也可以使用提交表單的方式。這時請求方法一般是 POST,當然春哥也無法阻止你用 GET。在前面我們雖然介紹過處理 POST 請求的例子,但那只是利用了 JAX-RS 對 JAXB 的支持,并沒有涉及到對具體請求參數的注入。JAX-RS 提供了 @FormParam 注解來注入 POST 請求的參數,例如:

@POST
public Response createMovie(@FormParam("title") String title) {
    // 此處省略若干行
}
    

這兒省略了 @Consumes 注解,JAX-RS 會自動默認為 @Consumes(MediaType.APPLICATION_FORM_URLENCODED),也就是 application/x-www-form-urlencoded 格式的請求。如果請求格式為 multipart/form-data,就必須顯示指明:

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response createMovie(@FormParam("title") String title) {
    // 此處省略若干行
}
    

JAX-RS 還支持文件的上傳和下載,以后再介紹。

HTTP 頭部參數

注入 HTTP 頭部參數簡單得不能再簡單了:

@GET
@Path("xxx")
@Produces(MediaType.TEXT_PLAIN)
public String xxx(@HeaderParam("User-Agent") String userAgent) {
    // 此處省略若干行
}
    

如果有很多頭部參數,為了避免臃腫的參數列表,可以注入一個頭部對象,然后編程訪問頭部參數:

@GET
@Path("xxx")
@Produces(MediaType.TEXT_PLAIN)
public String xxx(@Context HttpHeaders headers) {
    // 此處省略若干行
}
    

Cookie 參數

注入 Cookie 參數同樣的簡單:

@GET
@Path("xxx")
@Produces(MediaType.TEXT_PLAIN)
public String xxx(@CookieParam("userName") String userName) {
    // 此處省略若干行
}
    

如果希望編程訪問,則可以像編程訪問那樣注入一個 HttpHeaders 對象,然后通過它的 getCookies() 方法來獲取所有的 Cookie。



蜀山兆孨龘 2011-12-29 16:34 發表評論
]]>
JAX-RS 從傻逼到牛叉 5:資源的動態定位http://www.tkk7.com/shinzey/archive/2011/12/21/366802.html蜀山兆孨龘蜀山兆孨龘Wed, 21 Dec 2011 08:00:00 GMThttp://www.tkk7.com/shinzey/archive/2011/12/21/366802.htmlhttp://www.tkk7.com/shinzey/comments/366802.htmlhttp://www.tkk7.com/shinzey/archive/2011/12/21/366802.html#Feedback0http://www.tkk7.com/shinzey/comments/commentRss/366802.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/366802.html

目前我們的電影服務只提供了對電影信息的訪問服務,現在我們要再增加兩項級服務,分別用來訪問導演和演員信息。加上原先的電信信息服務,我們把 URI 統一放到 /ms/rest/service/ 的子路徑下。最先想到的方法就是為這三個 URI 分別寫 JAX-RS 服務:

@Singleton
@Path("service/movie")
public class MovieService {
    // 此處省略若干行
}

@Singleton
@Path("service/director")
public class DirectorService {
    // 此處省略若干行
}

@Singleton
@Path("service/director")
public class ActorService {
    // 此處省略若干行
}
    

這種寫法的缺點就是讓三個本來有點關系(父級 URI 相同)的服務被放到了毫不相干的三個類里面,不一個個類地查看注解難以看出這點關系。為此,JAX-RS 提供了動態資源綁定的功能,讓我們能夠對這種情況做一些整理。

首先,我們引入一個服務定位器來處理集中管理這三個子級服務:

@Singleton
@Path("service")
public class ServiceLocator {
    @Inject
    private MovieService movieService;
    @Inject
    private DirectorService directorService;
    @Inject
    private ActorService actorService;
    private Map<String, Object> serviceMap;

    @PostConstruct
    private initServiceMap() {
        serviceMap = new HashMap<>();
        serviceMap.put("movie", movieService);
        serviceMap.put("director", directorService);
        serviceMap.put("actor", actorService);
    }

    @Path("{name}")
    public Object locateService(@PathParam("name") String name) {
        Object service = serviceMap.get(name);
        if (service == null) {
            throw new WebApplicationException(Status.SERVICE_UNAVAILABLE);
        }
        return service;
    }
}
    

該類中的 locateService 方法根據服務的名稱返回相應的服務實例,注意該方法只有一個 @Path 注解,因為它并不清楚請求的具體內容;返回對象的類型為 Object,表明動態資源定位不要求服務類實現相同的接口,只需要它們的方法帶有相應的 JAX-RS 注解,就能夠被 JAX-RS 自動發現和處理(專業術語稱為 introspect,內?。?,以 MovieService 為例:

@Singleton
public class MovieService {
    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Movie find(@PathParam("id") int id) {
        Movie movie = movieDao.get(id);
        if (movie != null) {
            return movie;
        } else {
            throw new WebApplicationException(Status.NOT_FOUND);
        }
    }

    // 此處省略若干行
}

這樣,每個請求實際上都由兩個類先后處理。例如,處理請求 GET /ms/rest/service/movie/1 的時候,先由 ServiceLocator 返回相配的服務實例 movieService,然后再由該實例的 find 方法返回結果。比起最開始那三個簡單的類,雖然多了一層調用,但換來了更加清晰的結構。

動態資源定位是一個非常靈活強大的功能,用好的話,完全可以把 URI 層次整理成一個類似于文件目錄結構的抽象文件系統。



蜀山兆孨龘 2011-12-21 16:00 發表評論
]]>
JAX-RS 從傻逼到牛叉 4:路徑優先級規則http://www.tkk7.com/shinzey/archive/2011/12/07/365769.html蜀山兆孨龘蜀山兆孨龘Wed, 07 Dec 2011 07:10:00 GMThttp://www.tkk7.com/shinzey/archive/2011/12/07/365769.htmlhttp://www.tkk7.com/shinzey/comments/365769.htmlhttp://www.tkk7.com/shinzey/archive/2011/12/07/365769.html#Feedback0http://www.tkk7.com/shinzey/comments/commentRss/365769.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/365769.html

籠子大了什么鳥都有。同樣的道理,不論多么細心地設計 URI 結構,在系統復雜到一定程度后,仍然難以避免路徑沖突。為此,JAX-RS 使用一些規則來定義路徑匹配的優先級。

如果某個請求路徑可以對上多個 URI 匹配模式,那么 JAX-RS 就把可能匹配上的 URI 模式先拼接完整,按照下列規則依次進行比較,直到找出最適合的匹配模式:

  1. 首先,字面字符數量更多的 URI 模式優先?!白置孀址本褪菍懰赖穆窂蕉危话窂椒指舴?/ 和模板參數。例如 /ms/rest/movie/{id : \\d+} 包含 11 個字面字符。
  2. 其次,模板參數個數最多的 URI 模式優先。例如 /ms/rest/movie/{id : \\d+} 帶一個模板參數。
  3. 最后,含正則表達式的模板參數個數最多的 URI 模式優先。例如 /ms/rest/movie/{id : \\d+} 帶一個含正則表達式的模板參數。

現在看一個例子?;仡櫼幌?,/ms/rest/movie/{id : \\d+} 已經用來根據 ID 獲取電影信息。為了制造麻煩,現在引入 /ms/rest/movie/{title} 來根據電影標題獲取電影信息。先請你猜一猜 /ms/rest/movie/300 代表啥?ID 為 300 的神秘電影,還是我們可愛的勇士?只能跟著規則一條一條地看:

  1. 首先,兩個 URI 匹配模式的字面字符都是 11,下一步。
  2. 其次,兩個 URI 匹配模式都帶一個模板參數,下一步。
  3. 最后,只有 /ms/rest/movie/{id : \\d+} 帶了一個含正則表達式的模板參數,勝利!所以返回 ID 為 300 的片片。

傳說這三條規則能夠覆蓋 90% 以上的情景。不過我們馬上就能造出一個打破規則的東西:/ms/rest/movie/{title : [ \\w]+}。經過測試,/ms/rest/movie/300 會匹配上 /ms/rest/movie/{id : \\d+}。如何解釋?JAX-RS 規范文檔 3.7.2 定義了完整的匹配規則,對于這兩個簡單的 URI 匹配模式,似乎一直進行到底都無法比較出優先級。莫非有另外的潛規則?或者是 JAX-RS 的實現(參考實現為 Jersey)自行規定?但無論如何,搞出這種怪物本身就是一個設計錯誤,所以也不必去深究原因。



蜀山兆孨龘 2011-12-07 15:10 發表評論
]]>
JAX-RS 從傻逼到牛叉 3:路徑匹配http://www.tkk7.com/shinzey/archive/2011/10/09/360199.html蜀山兆孨龘蜀山兆孨龘Sun, 09 Oct 2011 04:43:00 GMThttp://www.tkk7.com/shinzey/archive/2011/10/09/360199.htmlhttp://www.tkk7.com/shinzey/comments/360199.htmlhttp://www.tkk7.com/shinzey/archive/2011/10/09/360199.html#Feedback1http://www.tkk7.com/shinzey/comments/commentRss/360199.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/360199.html

JAX-RS 的核心功能是處理向 URI 發送的請求,所以它提供了一些匹配模式以便簡化對 URI 的解析。樓主在本系列的上一篇文章中已經使用了最簡單的路徑參數,本文將介紹一些稍微高級點的咚咚。

模板參數

前面已經見過用 @Path("{id}")@PathParam("id") 來匹配路徑參數 id。這種匹配方式可以被嵌入到 @Path 注解中的任何地方,從而匹配多個參數,例如下面的代碼用來查找 ID 在某一范圍內的電影:

        @GET
        @Path("{min}~{max}")
        @Produces(MediaType.APPLICATION_JSON)
        public List<Movie> findMovies(@PathParam("min") int min, @PathParam("max") int max) {
    

于是,GET /ms/rest/movie/5~16 就將返回 ID 為 5 到 16 的電影。此處的 minmax 已被自動轉換為 int 類型。JAX-RS 支持多種類型的自動轉換,詳見 @PathParam 的文檔。

根據 HTTP 規范,參數可能會編碼。默認情況下,JAX-RS 會自動解碼。如果希望得到未解碼的參數,只需在參數上再加個 @Encoded 注解。該注解適用于大多數 JAX-RS 注入類型,但并不常用。

模板參數雖然靈活,也可能會帶來歧義。例如想用 {firstName}-{lastName} 匹配一個人的姓名,但恰好某人的名(lastName)含有“-”字符,像 O-live K 這種,匹配后就會變成姓 live-K,名 O。這種場景很難避免,一種簡單的解決方法就是對參數值進行兩次編碼,然后在服務端代碼解碼一次,因為 JAX-RS 默認會進行一次解碼,或者加上 @Encoded 注解,自己進行兩次解碼。

另外,在一個復雜系統中,多個 @Path 可能會造成路徑混淆,例如 {a}-{a}-z 都能匹配路徑 a-z。雖然 JAX-RS 定義了一些規則來指定匹配的優先級,但這些規則本身就比較復雜,并且也不能完全消除混淆。樓主認為,設計一個 REST 系統的核心就是對 URI 的設計,應當小心處理 URI 的結構,合理分類,盡量保證匹配的唯一性,而不要過度使用晦澀的優先級規則。樓主將在下一篇文章介紹優先級規則。

正則表達式

模板參數可以用一個正則表達式進行驗證,寫法是在模板參數的標識符后面加一個冒號,然后跟上正則表達式字符串。例如在根據 ID 查詢電影信息的代碼中,模板參數 {id} 只能是整數,于是代碼可以改進為:

        @GET
        @Path("{id : \\d+}")
        @Produces(MediaType.APPLICATION_JSON)
        public List<Movie> findMovies(@PathParam("min") int min, @PathParam("max") int max) {
    

冒號左右的空格將被忽略。用正則表達式驗證數據很有局限性,可惜 JAX-RS 目前并不能直接集成 Bean 驗證框架,因此復雜的驗證只能靠自己寫代碼。

查詢參數

查詢參數很常見,就是在 URI 的末尾跟上一個問號和一系列由“&”分隔的鍵值對,例如查詢 ID 為 5 到 16 的電影也可以設計為 /ms/rest/movie?min=5&max=16。JAX-RS 提供了 QueryParam 來注入查詢參數:

        @GET
        @Produces(MediaType.APPLICATION_JSON)
        public List<Movie> findMovies(@DefaultValue("0") @QueryParam("min") int min,
                @DefaultValue("0") @QueryParam("max") int max) {
    

查詢參數是可選的。如果 URI 沒有設定某個查詢參數,JAX-RS 就會根據情況為其生成 0、空字符串之類的默認值。如果要手動設定默認值,需要像上面的代碼一樣用 @DefaultValue 注解來指定。另外還可以加上 Encoded 注解來得到編碼的原始參數。

有的查詢參數是一對多的鍵值對,例如 /xyz?a=def&a=pqr,這種情況只需將注入的參數類型改為 List 即可。

矩陣參數

矩陣參數應該屬于 URI 規范中的非主流類型,但它實際上比查詢參數更靈活,因為它可以嵌入到 URI 路徑中的任何一段末尾(用分號隔開),用來標識該段的某些屬性。例如 GET /ms/rest/movie;year=2011/title;initial=A 表示在 2011 年出品的電影中查找首字母為 A 的標題。year 是電影的屬性,而 initial 是標題的屬性,這比把它們都作為查詢參數放在末尾更直觀可讀。匹配 URI 的時候,矩陣參數將被忽略,因此前面的 URI 匹配為 /ms/rest/movie/title。矩陣參數可以用 @MatrixParam 來注入:

        @GET
        @Path("title")
        @Produces(MediaType.APPLICATION_JSON)
        public List<String> findTitles(@MatrixParam("year") int year,
                @MatrixParam("initial") String initial) {
    

如果 URI 的多個段中含有相同名稱的矩陣參數,例如 /abc;name=XXX/xyz;name=OOO,這種直接注入就失效了,只能用下面要講的編程式訪問來取得。

編程式訪問

如果簡單的注入不能達到目的,就需要通過注入 PathSegmentUriInfo 對象來直接編程訪問 URI 的信息。

一個 PathSegment 對象代表 URI 中的一個路徑段,可以從它得到矩陣參數。它可以通過 @PathParam 來注入,這要求該路徑段必須整個被定義為一個模板參數。例如下面的代碼也可以用來處理 GET /ms/rest/movie/{id}

        @GET
        @Path("{id}")
        @Produces(MediaType.APPLICATION_JSON)
        public Movie findMovie(@PathParam("id") PathSegment ps) {
    

@PathParam 也可以注入多個段,如果想把 /a/b/c/d 匹配到 /a/{segments}/d,直接注入一個字符串顯然不行,因為 b/c 是兩個路徑段。唯一的選擇是把注入的類型改為 List<PathSegment>。樓主嚴重不推薦用一個模板參數匹配多個路徑段,因為這很容易干擾其他匹配的設計,最后搞成一團亂麻。URI 路徑段應當盡量設計得簡單明晰,再輔以矩陣參數或查詢參數就能應付大多數場景。不論對服務端還是客戶端開發人員來說,簡潔的 URI 既便于管理,又便于使用。網上有不少關于 URI 設計指南的文章,此處不再贅述。

如果想完全手動解析路徑,則可以用 @Context 注入一個 UriInfo 對象,通過此對象可以得到 URI 的全部信息,詳見 API 文檔。例如:

        @GET
        @Path("{id}/{segments}")
        @Produces(MediaType.PLAIN_TEXT)
        public String getInfo(@PathParam("id") int id, @Context UriInfo uriInfo) {
    

UriInfo 主要用在某些特殊場合下起輔助作用,設計良好的 URI 用普通的注入就能完成大部分匹配。


工欲善其事必先利其器,為此 JAX-RS 提供了這些利器來解析 URI。至于如何用這些器來做出一個好系統,則還是依賴于 URI 本身的設計。



蜀山兆孨龘 2011-10-09 12:43 發表評論
]]>
JAX-RS 從傻逼到牛叉 2:開發一個簡單的服務http://www.tkk7.com/shinzey/archive/2011/09/20/359085.html蜀山兆孨龘蜀山兆孨龘Tue, 20 Sep 2011 09:22:00 GMThttp://www.tkk7.com/shinzey/archive/2011/09/20/359085.htmlhttp://www.tkk7.com/shinzey/comments/359085.htmlhttp://www.tkk7.com/shinzey/archive/2011/09/20/359085.html#Feedback0http://www.tkk7.com/shinzey/comments/commentRss/359085.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/359085.html

JAX-RS 使用注解進行配置,所以用它開發 REST 風格的服務非常簡單。樓主在本文用一個小例子來說明 JAX-RS 的基本用法。


假設樓主要開發一個小電影服務,客戶端可以通過請求 URI 對電影進行 CRUD 操作。為簡明起見,這兒不使用數據庫,只在內存中模擬。先用一個非常簡單的 Movie 類,在后續的文章中根據情況逐步擴充:

        public class Movie {
            private int id;
            private String title;
            // 此處省略若干行
        }
    

嗯,就是一個很普通的 JavaBean,實際項目中可以根據需要加上 @Entity 等注解。接下來看看如何編寫 JAX-RS 服務。


一個 JAX-RS 服務就是一個使用了 JAX-RS 注解來將 HTTP 請求綁定到方法的 Java 類,一共支持兩種類型:單請求對象或單例對象。單請求對象意味著每來一個請求,就創建一個服務對象,在請求結束時銷毀。單例對象則意味著只有一個服務對象處理所有的請求,從而可以在多個請求間維持服務狀態。JAX-RS 服務可通過繼承 javax.ws.rs.core.Application 來定義,其中的 getClasses 方法返回單請求對象的類型,getSingletons 方法返回單例對象的類型。這兩個方法是可選的。在 Java EE 6 環境中,如果這兩個方法都返回 null 或者空集合,那么應用程序中的所有 JAX-RS 都將被部署。這時可以用 CDI 的 @javax.inject.Singleton 或者 EJB 的 @javax.ejb.Singleton 注解來指定單例對象。

如果電影服務的上下文根路徑為 http://localhost/ms,而樓主希望將服務部署到 http://localhost/ms/rest 下面,只需要寫一個類:

        @ApplicationPath("rest")
        public class RestApplication extends Application {
        }
    

@ApplicationPath 注解指定所有服務的相對基址,如果為空字符串,則直接使用上下文根路徑。另一種配置方式是在 web.xml 文件中進行聲明,那是為了使 JAX-RS 能在 Servlet 容器(例如 Tomcat)中運行,此處略過。這項配置必不可少,否則無法部署服務。


很好很強大,現在開始編寫電影服務類 MovieService,先看看聲明和初始化:

        @Singleton
        @Path("movie")
        public class MovieService {
            private AtomicInteger ai;
            private ConcurrentMap<Integer, Movie> movieMap;

            @PostConstruct
            private void init() {
                ai = new AtomicInteger();
                movieMap = new ConcurrentHashMap<>();
                int id = ai.getAndIncrement();
                movieMap.put(id, new Movie().setId(id).setTitle("Avatar"));
            }
    

因為樓主只需要一個“內存數據庫”,所以用單例對象即可,此處使用 CDI 的 @javax.inject.Singleton 來聲明單例。@Path 聲明了一個服務,它指示 MovieService 負責處理發送到 http://localhost/ms/rest/movie 的請求。路徑的拼接方式非常直觀。init 方法帶有 @PostConstruct 注解,因此將在 MovieService 構造完成后立即調用,它向 movieMap 中存入了一個 ID 為 0 的 Movie 對象。為簡化代碼,Movie 的設置方法都返回 this,有點偽造構建者模式的味道。


接下來看看如何處理 HTTP 請求。

GET

GET 請求用于獲取一個或多個資源。在本例中用來獲取一部電影的信息:

        @GET
        @Path("{id}")
        @Produces(MediaType.APPLICATION_JSON)
        public Movie find(@PathParam("id") int id) {
            Movie movie = movieMap.get(id);
            if (movie != null) {
                return movie;
            } else {
                throw new WebApplicationException(Response.Status.NOT_FOUND);
            }
        }
    

該方法標注了 @GET,表示用來處理向 http://localhost/ms/rest/movie/{id} 發送的 GET 請求。@Path 再次用來綁定路徑,注意其參數 {id},它帶有花括號,對應 URI 的最后一段,也正好和方法參數 id@PathParam 的值相對應。這種參數還有很多高級用法,以后再介紹。@Produces 注解指定輸出格式為 JSON。JAX-RS 內置了很多格式,詳見 MediaType 的文檔。如果找到了相應 ID 的對象,則將其返回,JAX-RS 會自動加上響應碼 200 OK;否則拋出異常,錯誤碼為 404 Not Found。

例如,通過瀏覽器訪問 http://localhost/ms/rest/movie/0,得到的結果為 {"@id":"0","@title":"Avatar"}。

POST

POST 請求用于創建一個資源。在本例中用來創建一部電影:

        @POST
        @Consumes(MediaType.APPLICATION_JSON)
        public Response create(Movie movie) {
            int id = ai.getAndIncrement();
            movieMap.put(id, movie.setId(id));
            return Response.created(URI.create(String.valueOf(id))).build();
        }
    

由于沒有 @Path 注解,所以 POST 請求的目標就直接是 http://localhost/ms/rest/movie。Consumes@Produces 相反,表示接受的數據類型,此處 JAX-RS 會自動把 JSON 數據轉換為 Movie 對象。返回的響應碼為 201 Created,并且帶有所創建資源的 URI。

例如,向 http://localhost/ms/rest/movie 發送 POST 請求,正文為 {"@title": "007"},則可以從 FireBug 的網絡監控中看到返回的響應碼,以及頭部中 Location 的值為 http://localhost:8080/rest/service/movie/1。多次發送該 POST 請求,將會創建多個資源,以保證 POST 不是冪等的。

PUT

PUT 請求用于創建或更新一個資源。與 POST 不同,PUT 請求要指定某個特定資源的地址。在本例中用來更新一部電影的信息:

        @PUT
        @Path("{id}")
        @Consumes(MediaType.APPLICATION_JSON)
        public Response update(@PathParam("id") int id, Movie movie) {
            movie.setId(id);
            if (movieMap.replace(id, movie) != null) {
                return Response.ok().build();
            } else {
                throw new WebApplicationException(Response.Status.NOT_FOUND);
            }
        }
    

更新成功就返回 200 OK,否則返回 404 Not Found。這兒先把 movie 對象的 ID 強制改為 URI 所指定的,以免出現不一致。也可以根據需求,將不一致作為異常處理,給客戶端返回一個錯誤碼。

順便啰嗦一句,反正代碼在自己手中,樓主也可以把 PUT 搞成非冪等的,例如將 PUT 當成 POST 來處理,就像以前把 GET 和 POST 一視同仁那樣。不過咱既然在搞 JAX-RS,就還是要沾染一點 REST 風格,嚴格遵守 HTTP 才是。

DELETE

DELETE 請求用于刪除一個資源。在本例中用來刪除一部電影:

        @DELETE
        @Path("{id}")
        public Response delete(@PathParam("id") int id) {
            if (movieMap.remove(id) != null) {
                return Response.ok().build();
            } else {
                throw new WebApplicationException(Response.Status.NOT_FOUND);
            }
        }
    

沒什么特別的,該說的前面都說了。

HEAD 和 OPTIONS 請求就忽略吧,用得不太多,也同樣挺簡單的。


JAX-RS 服務的部署和部署常規 Web 程序一樣,打包成 war 文件就可以了。最后贊一下 NetBeans 可以為 REST 風格的服務自動生成測試頁面,很好用,雖然在 Firefox 下頁面顯示不正常(對此我已經提了一個 bug),但 IE 是可以的。



蜀山兆孨龘 2011-09-20 17:22 發表評論
]]>
JAX-RS 從傻逼到牛叉 1:REST 基礎知識http://www.tkk7.com/shinzey/archive/2011/09/16/358799.html蜀山兆孨龘蜀山兆孨龘Fri, 16 Sep 2011 07:31:00 GMThttp://www.tkk7.com/shinzey/archive/2011/09/16/358799.htmlhttp://www.tkk7.com/shinzey/comments/358799.htmlhttp://www.tkk7.com/shinzey/archive/2011/09/16/358799.html#Feedback3http://www.tkk7.com/shinzey/comments/commentRss/358799.htmlhttp://www.tkk7.com/shinzey/services/trackbacks/358799.html

JAX-RS(JSR 311 - Java™ API for RESTful Web Services,用于 REST 風格的 Web 服務的 Java™ API)是 Java EE 6 規范的一部分,其目標在于簡化和標準化用 Java 開發 REST 風格的 Web 服務。雖然 Java EE 6 剛出爐的時候,樓主也從頭到尾看過這份規范,但苦于沒有實際的項目練手,看過又忘了,現在最多算達到大成傻逼的境界。這次邊看邊寫,期望完成后至少能破入小成牛逼。先從 REST 本身開始。


REST(REpresentational State Transfer,代表性狀態傳輸)自稱是一種風格而非標準,這在樓主看來有炒作的嫌疑。如果僅僅是一種風格,那么不同的框架如何兼容?所以才有 JAX-RS 的誕生。REST 最大的貢獻是帶來了 HTTP 協議的復興。為什么叫復興呢?本來 HTTP 的功能挺豐富的,可惜長期以來只被用作傳輸數據,大好青年被埋沒了。樓主記得剛開始學 Servlet 的時候,一向是把 doGetdoPost 兩個方法一視同仁的,因為書上這么教,很多 Web 框架也這么搞,以至于弄了很久才搞清楚 GETPOST 是兩種不同的請求?,F在 REST 拍磚說道,HTTP 早就定義好了一堆操作,以前一直被混淆使用,現在應該重新賦予它們本來的意義了,而且如果充分發揮 HTTP 的功能,完全能夠勝任分布式應用的開發(傳說中的 SOA)。


SOA 的理念在于將系統設計為一系列可重用的、解耦的、分布式的服務。這也不是新鮮玩意兒了,早期有 CORBA,稍晚有 SOAP 等等。REST 作為后起之秀,能夠快速崛起,也必有其非同尋常的特色。下面一一列舉。

可尋址性(Addressability)

系統中的每個資源都可以通過唯一標識符來訪問。小插一句,“標識”的正確讀音是 biāozhì。REST 使用 URI(統一資源標識符)管理資源的地址。URI 的概念不解釋。一個 URI 可以指向一個或者多個資源。

統一的受限接口(The Uniform, Constrained Interface)

實際上強調了 HTTP 操作的原意。REST 主要使用了 GET、PUT、DELETE、POST、HEAD 和 OPTIONS 這 6 種操作。此處有兩個曾經被忽略的 HTTP 概念:冪等(idempotent)和安全(safe)。冪等應該是 HTTP 從數學借來的一個術語(原始的數學意義樓主也不懂),意味著若干次請求的副作用與單次請求相同,或者根本沒有副作用。GET、PUT、DELETE、HEAD 和 OPTIONS 都是冪等的:GET、HEAD 和 OPTIONS 都是讀操作,容易理解;PUT 用于創建或更新已知 URI 的資源,多次創建或更新同一個資源顯然和一次的效果相同;DELETE 刪除資源,亦然。安全表示操作不會影響服務器的狀態。GET、HEAD 和 OPTIONS 是安全的。POST 既不冪等又不安全,因為和 PUT 不同,POST 創建資源的時候并不知道資源的 URI,所以多個 POST 請求將會創建多個資源。

面向表象(Representation-Oriented)

表象這個詞有點拗口,傳聞在一個 REST 風格的系統中,服務端和客戶端之間傳輸的咚咚就是表象……表象可以是純文本、XML、JSON……或者自編的山寨格式。唉,不就是數據么?只不過可以用任意的格式來傳輸,因為 HTTP 正文里面啥都能放。Content-Type 頭用來聲明格式,一般是 MIME(多用途因特網郵件擴展),像 text/plain、text/htmlapplication/pdf 這些。MIME 可以帶屬性,例如 text/html; charset=UTF-8。

無態通信(Communicate Statelessly)

REST 服務器只管理資源,而不會像 Web 服務器一樣記錄客戶的會話狀態,這些應該由客戶端來管理,如此就能增強 REST 服務器的伸縮性。此處的客戶端可以是客戶端程序、瀏覽器,甚至一個 Web 應用。總之,REST 只負責庫管啦!

HATEOAS

猛詞砸來了!HATEOAS = Hypermedia As The Engine Of Application State,超媒體作為應用狀態的引擎,怎么看起來比 SaaS(Software as a Service,軟件作為服務)還要嚇人呢?超文本不過是一只紙老虎,超媒體也瞞不過樓主的天眼:超媒體就是是由文字、圖像、圖形、視頻、音頻……鏈成一體的大雜燴!很簡單的一個例子,有些坑爹的電影網站經常發布一些內嵌了廣告的電影,播放的時候會彈出廣告窗口,里面很多鏈接,你去點兩下就中招了:這個電影文件就算是超媒體。

其實這個詞最關鍵的地方是“狀態引擎”。例如樓主在去網購,先選了幾個東西,接下來可以干啥呢?可以繼續選、可以把購物車清空、可以結賬……樓主可以從現狀“轉換”到其他某些狀態,而控制狀態轉換的那個咚咚就被冠名為狀態引擎。多么聰明的詞匯??!樓主發現凡是高手都是造詞磚家呀!用超媒體來控制狀態轉換,就是 HATEOAS:你是要繼續看電影還是看廣告?看哪個廣告?自己慢慢考慮……


REST 相比 CORBA、SOAP、WS-* 之流確實獨樹一幟,但也難逃玩弄概念的嫌疑。記得大學里講數據庫的老師說過:“你們現在學了這么多理論,其實以后在工作中未必管用。在大街上隨便找一個軟件培訓學校出來的小伙子,他哪兒懂什么第二第三范式?。康珔s能把數據庫玩兒得飛轉!”



蜀山兆孨龘 2011-09-16 15:31 發表評論
]]>
主站蜘蛛池模板: 成人免费黄色网址| 日韩一级在线播放免费观看| 亚洲国色天香视频| 国产精品久久久久影院免费| 成人国产精品免费视频| 亚洲大片免费观看| 亚洲成年看片在线观看| 久久这里只精品99re免费| 亚洲成年网站在线观看| 亚洲一区二区女搞男| 毛片视频免费观看| 中文字幕视频在线免费观看| 亚洲精品第一综合99久久| 国产成人综合亚洲AV第一页| 永久免费AV无码国产网站| 国产精品午夜免费观看网站| 亚洲最大的黄色网| 亚洲综合av永久无码精品一区二区| 日韩免费精品视频| 人妻免费一区二区三区最新| 亚洲精品无码aⅴ中文字幕蜜桃| 日韩亚洲一区二区三区| 在线免费观看韩国a视频| 91在线老王精品免费播放| 一级毛片一级毛片免费毛片| 亚洲三级在线免费观看| 亚洲精品高清无码视频| 免费在线不卡视频| 一个人免费观看www视频在线| 另类免费视频一区二区在线观看| 国产精品亚洲精品爽爽| 亚洲一欧洲中文字幕在线| 亚洲国产精品一区二区久久hs| 国产区卡一卡二卡三乱码免费| 69式互添免费视频| 青青草原1769久久免费播放| 免费的黄色的网站| 亚洲hairy多毛pics大全| 亚洲啪啪免费视频| 亚洲精品国产啊女成拍色拍| 亚洲国产无套无码av电影|