資源定位之注解相關簡介
@QueryParam注解
| 接口描述 | 資源地址 |
|---|---|
| 分頁查詢列表數(shù)據(jù) | /query-resource/test?start=24&size=10 |
| 排序并分頁查詢列表收 | /query-resource/test?limit=5&sort=program |
| 查詢單項數(shù)據(jù) | /query-resource/test?id=9 |
JAX-RS2定義了@QueryParam注解來定義查詢參數(shù),如下表所示
| 接口描述 | 資源地址 |
|---|---|
| 分頁查詢列表數(shù)據(jù) | /query-resource/test?start=24&size=10 |
| 排序并分頁查詢列表收 | /query-resource/test?limit=5&sort=program |
| 查詢單項數(shù)據(jù) | /query-resource/test?id=9 |
1、分頁查詢
/**
*
* @param start 起始條目 參數(shù)都使用了final進行限制
* 符合Checkstyle風格 即輸入?yún)?shù)只作為邏輯算法的依據(jù)使用
* 其本身并不會再找個過程中被修改
* @param size 查詢的條目
* @return
*/
@Path("/page") //page?start=0&size=10 訪問
@GET
@Consumes(MediaType.APPLICATION_JSON) //指定請求返回的響應體為JSON
@Produces(MediaType.APPLICATION_JSON)
public User getByPaging(@QueryParam("start") final int start,@QueryParam("size") final int size){
return null;
}
2、排序查詢
/**
* @param limit 分頁查詢條目
* @param sortName 排序規(guī)則
* @return
*/
@Path("/order") //order?limit=10&sort=web 訪問
@GET
@Consumes(MediaType.APPLICATION_JSON) //指定請求返回的響應體為JSON
@Produces(MediaType.APPLICATION_JSON)
public User getByOrder(@QueryParam("limit") final int limit,@QueryParam("sort") final int sortName){
return null;
}
3、單項查詢
/**
* @param segId
* @return
*/
@Path("/query") //order?id=10 訪問
@GET
@Consumes(MediaType.APPLICATION_JSON) //指定請求返回的響應體為JSON
@Produces(MediaType.APPLICATION_JSON)
public User getByQuery(@QueryParam("id") final int segId){
return null;
}
@PathParam注解
| 接口描述 | 資源地址 |
|---|---|
| 基本路徑參數(shù) | /path-resource/Eric |
| J結合查詢參數(shù) | /path-resource/Eric?hometown=Luoma |
| 帶有標點符號的資源路徑 | /path-resource/199-1999 /path-resource/01,2012-12,2014 |
| 子資源變長的資源路徑 | /path-resource/Asia/china/northeast/liaoning/shenyang/huanggu //path-resource/q/restful;program=java;type=web /path-resource/q2/restful;program=java;type=web |
JAX-RS2定義@PathParam注解來定義路徑參數(shù)----每個參數(shù)對應一個子資源,示例列表如下:
| 接口描述 | 資源地址 |
|---|---|
| 基本路徑參數(shù) | /path-resource/Eric |
| J結合查詢參數(shù) | /path-resource/Eric?hometown=Luoma |
| 帶有標點符號的資源路徑 | /path-resource/199-1999 /path-resource/01,2012-12,2014 |
| 子資源變長的資源路徑 | /path-resource/Asia/china/northeast/liaoning/shenyang/huanggu //path-resource/q/restful;program=java;type=web /path-resource/q2/restful;program=java;type=web |
1、@Path注解
JAX-RS2定義@Path注解來定義資源路徑,@Path接收一個value參數(shù)來解析資源路徑地址,可以使用靜態(tài)定義的方式外,也可以使用動態(tài)變量的方式,格式:{參數(shù)名稱:正則表達式},例如資源地址:/path-resource、199-1999.參考示例如下
@Path("{from:\\d+}-{to:\\d+}") //order?id=10 訪問
@GET
@Consumes(MediaType.APPLICATION_JSON) //指定請求返回的響應體為JSON
@Produces(MediaType.APPLICATION_JSON)
public User getByCondition(@QueryParam("from") final Integer from, @PathParam("to") final Integer to) {
return null;
}
在來一個復雜的例子:/path-resource、01,2012-12,2014(引入了逗號(,))
@Path("{beginMonth:\\d+},{beginYear:\\d+}-{endMonth:\\d+},{endYear:\\d+}")
2、正則表達式
3、路徑配查詢
查詢參數(shù)和路徑參數(shù)在一個接口中配合使用,可以更便捷的完成資源定位。
/**
* /path-resource/Eric?hometown=Luoma
*
* @param user Eric
* @param hometown Luoma
* @return
*/
@Path("{user:[a-zA-Z][a-zA-Z_0-9]*}") //order?id=10 訪問
@GET
@Consumes(MediaType.APPLICATION_JSON) //指定請求返回的響應體為JSON
@Produces(MediaType.APPLICATION_JSON)
public User getUserInfo(@QueryParam("user") final String user,
@DefaultValue("Shen Yang") @QueryParam("hometown") final String hometown) {
return null;
}
4、路徑區(qū)間
路徑區(qū)間(PathSegment)是對資源地址更加靈活的支持,使資源類的一個方法可以支持更加廣泛的資源地址的請求,例如下面的例子
/path-resource/Asia/china/northeast/liaoning/shenyang/huanggu
/path-resource/Asia/china/northeast/liaoning/shenyang/tiexi
/path-resource/china/liaoning/shenyang
如上所示的資源地址中含有固定子資源(shenyang),和動態(tài)子資源兩部分,對于動態(tài)匹配變長的子資源地址,PathSegment類型的參數(shù)結合正則表達式將大顯身手,如下:
@Path("{region:.+}/shenyang/{district:\\w+}") //order?id=10 訪問
@GET
@Consumes(MediaType.APPLICATION_JSON) //指定請求返回的響應體為JSON
@Produces(MediaType.APPLICATION_JSON)
public User getByAddress(@PathParam("region")final List<PathSegment> region,
@PathParam("district") final String district) {
final StringBuilder result = new StringBuilder();
for (PathSegment pathSegment : region) {
result.append(pathSegment.getPath()).append("-");
}
result.append("shenyang-" + district);
return null;
}
@MatrixParam注解
通過@MatrixParam注解來逐一的定義參數(shù),即通過聲明方式來獲取,示例代碼如下:
///path-resource/q2/program=java;type=web
@Path("q2/{condition}") //order?id=10 訪問
@GET
@Consumes(MediaType.APPLICATION_JSON) //指定請求返回的響應體為JSON
@Produces(MediaType.APPLICATION_JSON)
public User getByCondition(@PathParam("condition") final PathSegment condition,
@MatrixParam("program") final String program,
@MatrixParam("type") final String type) {
return null;
}
@FormParam注解
JAX-RS2定義了@FormParam注解來定義表單參數(shù),相應的REST方法用以處理請求實體媒體類型為Content-Type:application/x-www-form-urlencoded的請求,示例代碼如下:
@Path("form-resource)
public class FormResource{
@Post
public String newPassword(
@DefaultValue("ruolan") @FormParam(FromResource.USER) final String user,
@Encoded @FormParam(FormParam.PW) final String password,
@Encoded @FormParam(FormParam.NPW) final String newPassword,
@FormParam(FormParam.VNPW) final String verification{
}
)
}
上述代碼中,newPassword()方法是@FormParam注解定義了user等4個參數(shù),這些參數(shù)是容器請求中獲取并且匹配的,測試代碼示例如下:
@Test
public void testPost(){
final Form form = new Form();
form.param(FormResource.USER,"ruolan");
form.param(FormResource.PW,"北京");
form.param(FormResource.NPW,"上海");
form.param(FormResource.VNPW,"上海");
final String result = target.("form-resource").request()
.post(Entity.entity(form,MediaType.APPLICATION_FORM_URLENCODED_TYPE),String.class);
FormTest.LOGGER.debug(result);
Assert.assertEquals("encoded should let id to disable decoding",
"ruolan:%E5%8C%97%E4%BA%AC:%E4%B8%8A%E6%B5%B7:上海",result);
}
注意:
- @Encoded注解用以標識禁用自動解碼,示例中的測試結果中%E4%B8%8A%E6%B5%B7是newPassword()方法中的參數(shù)值"上海"的編碼值,當對newPassword使用@Encoded注解,REST方法得到的參數(shù)值就不會被編碼
- @DefaultValue注解,用以為客戶端沒有為其提供值的參數(shù) 提供默認的參數(shù)
@BeanParam注解
JAX-RS2定義了@BeanParam注解用于自定義參數(shù)組合,使REST方法可以使用簡潔的參數(shù)形式完成復雜的接口設計
public String getByAddress(@BeanParam Jaxrs2GuideParam param) {
//關注點2:參數(shù)組合
public class Jaxrs2GuideParam {
@HeaderParam("accept")
private String acceptParam;
@PathParam("region")
private String regionParam;
@PathParam("district")
private String districtParam;
@QueryParam("station")
private String stationParam;
@QueryParam("vehicle")
private String vehicleParam;
public void testBeanParam() {
...
final WebTarget queryTarget = target(path).path("China").path("northeast")
.path("shenyang").path("tiexi")
.queryParam("station", "Workers Village").queryParam("vehicle", "bus");
result = queryTarget.request().get().readEntity(String.class);
//關注點3:查詢結果斷言
Assert.assertEquals("China/northeast:tiexi:Workers Village:bus", result);
}
//關注點4:復雜的查詢請求
http://localhost:9998/ctx-resource/China/shenyang/tiexi?station=Workers+Village&vehicle=bus
在這段代碼中,getByAddress()方法只用了一個使用@BeanParam注解定義的Jaxrs2GuideParam類型的參數(shù),見關注點1;Jaxrs2GuideParam類定義了一系列REST方法會用到的參數(shù)類型,包括示例中使用的查詢參數(shù)"station"和路徑參數(shù)"region"等,從而使得getByAddress()方法可以匹配更為復雜的資源路徑,見關注點2;在變長子資源的例子基礎上,增加了查詢條件,但測試方法testBeanParam()發(fā)起的請求的資源地址見關注點4;可以看出這是一個較為復雜的查詢請求。其中路徑部分包括China/shenyang/tiexi,查詢條件包括station=Workers+Village和vehicle=bus。這些條件均在Jaxrs2GuideParam類中可以匹配,因此從關注點3的測試斷言中可以看出,該請求響應的預期結果是
"China/northeast:tiexi:Workers Village:bus"。
@CookieParam注解
JAX-RS2定義了@CookieParam注解用以匹配Cookie中的鍵值對信息,示例如下。
@GET
public String getHeaderParams(@CookieParam("longitude") final String longitude,
@CookieParam("latitude") final String latitude,
@CookieParam("population") final double population,
@CookieParam("area") final int area) {//關注點1:資源方法入?yún)?
return longitude + "," + latitude + " population=" + population + ",area=" + area;
@Test
public void testContexts() {
final Builder request = target(path).request();
request.cookie("longitude", "123.38");
request.cookie("latitude", "41.8");
request.cookie("population", "822.8");
request.cookie("area", "12948");
result = request.get().readEntity(String.class);
//關注點2:測試結果斷言
Assert.assertEquals("123.38,41.8 population=822.8,area=12948", result);
}
在這段代碼中,getHeaderParams()方法包含4個使用@CookieParam注解定義的參數(shù),用于匹配Cookie的字段,見關注點1;在測試方法testContexts中,客戶端Builder實例填充了相應的cookie鍵值對信息,其斷言是對cookie字段值的驗證,見關注點2。
@Context注解
JAX-RS2定義了@Context注解來解析上下文參數(shù),JAX-RS2中有多種元素可以通過@Context注解作為上下文參數(shù)使用,示例代碼如下。
public String getByAddress(
@Context final Application application,
@Context final Request request,
@Context final javax.ws.rs.ext.Providers provider,
@Context final UriInfo uriInfo,
@Context final HttpHeaders headers){
在這段代碼中,分別定義了Application、Request、Providers、UriInfo和HttpHeaders等5種類型的上下文實例。從這些實例中可以獲取請求過程中的重要參數(shù)信息,示例代碼如下。
final MultivaluedMap<String, String> pathMap = uriInfo.getPathParameters();
final MultivaluedMap<String, String> queryMap = uriInfo.getQueryParameters();
final List<PathSegment> segmentList = uriInfo.getPathSegments();
final MultivaluedMap<String, String> headerMap = headers.getRequestHeaders();
在這段代碼中,UriInfo類是路徑信息的上下文,從中可以獲取路徑參數(shù)集合getPath-Parameters()和查詢參數(shù)集合getQueryParameters()。類似地,我們可以從HttpHeaders類中獲取頭信息集合getRequestHeaders()。這些業(yè)務邏輯處理中常用的輔助信息的獲取,要通過@Context注解定義方法的參數(shù)或者類的字段來實現(xiàn)。
處理響應
REST的響應處理結果應包括響應頭中HTTP狀態(tài)碼,響應實體中媒體參數(shù)類型和返回值類型,以及異常情況處理。JAX-RS2支持4中返回值類型的響應,分別是無返回值、返回Response類實例、返回GenericEntity類實例和返回自定義類的實例.
VOID(無返回值類型)
在返回值類型中是void的響應,其響應實體為空,HTTP狀態(tài)碼是204。例子代碼如下:
@DELETE
@Path("{s}")
public void deleteTest(@PathParam("s") final String s){
}
因為delete操作不需要返回值關于資源標書的信息,因此該方法沒有返回值.(疑問點,如果需要通知客戶端是否刪除成功了沒有呢??????)
RESPONSE(Response返回值類型)
在Response的響應中,響應實體為Response類的entity()方法定義的實體實例。如果該內容為空,則HTTP狀態(tài)碼是204,否則HTTP狀態(tài)碼為200 OK,示例代碼如下:
@POST
@Path("c")
public Response getResponse(final String s){
return Response.ok().entity("char[] : " + s ).build();
}
在上述的代碼中,Response首先定義了HTTP的狀態(tài)碼為OK,然后填充實體信息,最后調用build()方法構建Response實例
GenericEntity(GenericEntity返回值類型)
通用實體類型作為返回值的情況并不是很常用。其形式是構造一個統(tǒng)一的實體實例并將其返回,實體實例作為第一個參數(shù),該實體類型作為第二個參數(shù),示例代碼如下:
@POST
@Path("b")
public String getGenericEntity(final byte[] bytes) {
return "byte[] : " + new String(bytes);
}
public GenericEntity<String> getGenericTest(final byte[] bytes){
//構建GenericEntity實例
return new GenericEntity<>("byte[] : " + new String(bytes),String.class );
}
自定義類型
JDK中的類(例如File,String等)都可以作為返回值類型,更常用的是返回自定義的POJO類型。示例代碼如下:
@POST
@Path("f")
public File getFile(final File f) throws FileNotFoundException, IOException {
BufferedReader br = new BufferedReader(new FileReader(f));
String s;
do {
s = br.readLine();
} while (s != null);
return f;
}
@POST
@Consumes({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
@Produces(MediaType.APPLICATION_XML)
public User getEntity(User user){
return user;
}
@POST
@Consumes({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
@Produces(MediaType.APPLICATION_XML)
public User getEntity(JAXBElement<User> user){
User user1 = user.getValue();
return user1;
}
處理異常
處理狀態(tài)碼
| 狀態(tài)碼 | 含義 |
|---|---|
| 200 OK | 服務器正常響應 |
| 201 Created | 創(chuàng)建新實體,響應頭Location指定訪問該實體的URL |
| 202 Accepted | 服務器接受請求,處理尚未完成??捎糜诋惒教幚頇C制 |
| 204 No Content | 服務器正常響應,但是響應實體為空 |
| 301 Moved Permanently | 請求資源的地址發(fā)生永久變動,響應頭Location指定新的URL |
| 302 Found | 請求資源的額地址發(fā)生臨時變動 |
| 304 Not Modified | 客戶端緩存資源依然有效 |
| 400 Bad Request | 請求信息出現(xiàn)語法錯誤 |
| 401 Unauthorized | 請求資源無法授權給未驗證錯誤 |
| 403 Frobidden | 請求資源未授權當前用戶 |
| 404 Not Found | 請求資源不存在 |
| 405 Method Not Allowed | 請求方法不匹配 |
| 406 Not Acceptable | 請求資源的媒體類型不匹配 |
| 500 Internale Server Error | 服務器內部錯誤,意外終止響應 |
| 501 Not Implemented | 服務器不支持當前請求 |
內容協(xié)商
@Produces注解
@Produces注解用于定義方法的響應實體的數(shù)據(jù)類型,可以定義一個或者多個,同事可以為每種類型定義質量因素(qualityfactor)。質量因素是取值范圍從0到1的小數(shù)值。如果不定義質量因素,那么該類型的質量因素默認為1
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_XML)
public User getJaxUser(@PathParam("id") final int userId) {
return new User(userId);
}
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public User getJsonUser(@PathParam("id") final int userId) {
return new User(userId);
}
/**
* 以下的代碼中 定義了xml和json兩種表述數(shù)據(jù)類型,xml的質量因素是0.5 json的質量因素是0.9
**/
@GET
@Path("book/{id}")
@Produces({"application/json;qs=0.9","application/xml;qs=0.6"})
public User getUser(@PathParam("id") final int userId) {
return new User(userId);
}
如果客戶端的請求中,明確接收的數(shù)據(jù)類型是兩者之一,響應實體使用指定類型.如果沒有定義或者兩者都定義且JSON的質量因素大雨或者等于XML,則返回JSON,還有一種用例就是,兩者都定義但是json的質量因素小于XML,內容協(xié)商的結果按照客戶端的喜好選擇相應實體的數(shù)據(jù)類型(xml格式)
@Consumes注解
@Consumes注解用于定義方法的請求實體的數(shù)據(jù)類型,和@Produces不同的是,@Consumes注解的數(shù)據(jù)類型的定義只用于JAX-RS2匹配請求處理的方法,不做內容協(xié)商使用,如果匹配不到,服務器會返回HTTP狀態(tài)碼415(Unsupported Meia Type),示例代碼如下:
@POST
@Consumes({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
@Produces(MediaType.APPLICATION_XML)
public User getEntity(User user){
final Builder request = Target(path).request();
final User result = request.post(Entity.entity(user,MediaType.APPLICATION_XML), User.class);
return user;
}
@Consumes媒體類型為XML格式和JSON格式,那么在客戶端的請求中,如果請求實體的數(shù)據(jù)類型定義是兩者之一,該方法會被選擇為處理請求的方法,否則查找是否有定義為相應數(shù)據(jù)類型的方法,如果沒有拋出javax.ws.rs.NotSupportedException異常,則使用該方法處理請求。