同類的文章網上有好多了,可是開發過程當中依然會有不少坑,以及由於對概念的不清晰把本身坑了的狀況。這篇文章會梳理我開發過程當中遇到的坑以及填坑的方法,附加對Jersey和Webservice一些概念的梳理,附加一些簡化開發流程的技巧,文末有相關引用。html
全部開發都伴隨着搭環境,而搭環境這個事情。。。有時能夠很複雜,jar包可能衝突,jar包可能跟服務器發生衝突,不一樣版本的Jar包使用起來也可能不同。。。java
1. Jersey 1.x 和 2.x項目內的config不一樣,且jersey 2.x 把 com.sun.jersey的路徑改爲了org.glassfish.jersey,這是最大的坑。筆者一開始沒注意,開着1.x的教程用了2.x的包,即便後來發現了這個問題,但因爲1.x陷的太深而改不回來了。mysql
同窗們會說,那改用1.x的包不就好啦。事實並不是那麼簡單,jersey1.1和1.9的配置也不一樣,且官方如今只提供1.9的下載,下載下來有N多jar包,好吧,按照教程作,結果tomcat蹦了。仔細查看教程的tomcat版本是6.0,我用的是8.0.linux
解決方案: 使用jersey-bundle,這就是等於把全部用獲得的jersey包打包成了一個bundle,避免了衝突等問題,網上能夠輕鬆下載到,我用的是jersey-bundle-1.17.1.jargit
2. Browser address bar只能發送get請求,這是用瀏覽器進行測試時遇到的一個大坑。github
這個坑參考http://www.cnblogs.com/Raymond-Yang/p/5412611.html,這裏再也不贅述web
---------------------------------------------------------更新 2016.4.27---------------------------------------------------------sql
1. 添加了項目的github地址(文末)數據庫
2. 添加了學習和開發項目心得(文末)json
3. 更新了jersey的配置xml
4. 更新了WebService的URI與類和方法的關係解釋
-------------------------------------------------------------------------------------------------------------------------------------
1. Eclipse Mars 開發前須要下載Eclipse Web開發包,參考Eclipse配置Tomcat服務器和Dynamic Web Project的問題
2. Tomcat 8.0, 同窗們可能遇到的坑,同見Eclipse配置Tomcat服務器和Dynamic Web Project的問題
3. Mysql 5.x 這個沒什麼問題
4. Jersey環境 jersey-bundle-1.17.1.jar google一搜就有,使用bundle能避免不少衝突和環境配置的坑。。推薦
5. 其餘
1. gson-2.3.1.jar
2. mysql-connector-java-5.1.38-bin.jar
下載下來拷到Project->WebContent->WEB-INF->lib
項目結構
爲何在這裏提測試環境呢。。。由於樓主以前遇到過一個大坑,經過browser去測試webservice,get還好,post就不行了,一直報405 Error。。我一直覺得是我哪裏寫錯了。。最後才發現。。實際上是瀏覽器地址欄只能發送get請求。。。具體參見用瀏覽器測試Get與Post Webservice,Post一直報405錯誤,而Get可以成功的緣由與解決方法
後期我就用client作測試了,輕鬆簡介。。
這個跟普通的mysql鏈接沒什麼區別,代碼以下
package com.qad.yab.dao; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class CVCJDBC { private Connection conn; public Connection getConn() throws ClassNotFoundException, SQLException { if (null != conn) { return conn; } else { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test1", "cvc", ""); System.out.println("connect success"); return conn; } } }
package com.qad.yab.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import com.qad.yab.dto.User; public class CVCUserDAO { private Connection conn; public ArrayList<User> getUsers() throws Exception { conn = new CVCJDBC().getConn(); String sql = "select * from user;"; PreparedStatement stmt = conn.prepareStatement(sql); ResultSet result = stmt.executeQuery(); ArrayList<User> datas = new ArrayList<User>(); while (result.next()) { User data = new User(); data.setId(result.getInt("id")); data.setLoginID(result.getString("LoginID")); data.setName(result.getString("name")); data.setEmail(result.getString("email")); datas.add(data); } stmt.close(); return datas; } public ArrayList<User> getUsers(String loginID) throws Exception { conn = new CVCJDBC().getConn(); String sql = "select * from user where LoginID = '" + loginID + "';"; PreparedStatement stmt = conn.prepareStatement(sql); ResultSet result = stmt.executeQuery(); ArrayList<User> datas = new ArrayList<User>(); while (result.next()) { User data = new User(); data.setId(result.getInt("id")); data.setLoginID(result.getString("LoginID")); data.setName(result.getString("name")); data.setEmail(result.getString("email")); datas.add(data); } stmt.close(); return datas; } public void createUser(String loginId, String name, String email) { try { conn = new CVCJDBC().getConn(); String sql = "insert into user (LoginID, name, email) values(?, ?, ?)"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, loginId); stmt.setString(2, name); stmt.setString(3, email); stmt.execute(); stmt.close(); } catch (Exception e) { System.err.println("ERROR: Exception in create User"); } } }
這裏用了一個User的Object,裏面有loginID, name, email和active,也沒什麼特別的。
package com.qad.yab.dto; public class User { private int id; private String loginID; private String name; private String email; private boolean active; /** * Default constructor to create new user */ public User() { super(); } /** * Create new user with user name * * @param name * the login ID */ public User(String loginID) { super(); this.loginID = loginID; } /** * @return the id */ public int getId() { return id; } /** * @param id * the id to set */ public void setId(int id) { this.id = id; } /** * @return the loginID */ public String getLoginID() { return loginID; } /** * @param loginID * the loginID to set */ public void setLoginID(String loginID) { this.loginID = loginID; } /** * @return the name */ public String getName() { return name; } /** * @param name * the name to set */ public void setName(String name) { this.name = name; } /** * @return the email */ public String getEmail() { return email; } /** * @param email * the email to set */ public void setEmail(String email) { this.email = email; } /** * @return the active */ public boolean isActive() { return active; } /** * @param active * the active to set */ public void setActive(boolean active) { this.active = active; } }
好吧,這裏纔是本文的核心部分
先要理清幾個概念
1. Get和Post
Get通常是讀取數據,而Post通常是向服務器傳遞數據,具體能夠參見用瀏覽器測試Get與Post Webservice,Post一直報405錯誤,而Get可以成功的緣由與解決方法,裏面解釋的很詳細了。這裏用@GET和@POST來規範方法
2. @Path
這個就是Webservice的路徑,好比第一個@Path(」/user」),就能找到這個類,以後@Path("/getUser"),就能具體找到這個方法,獲取其返回值
3. @Produces & @Consumes
WebService最終返回何種類型的返回值?在@Produces中定義,通常使用於Get
相對的,WebService接受何種類型的參數?在@Consumes中定義,通常用於Post
4. @QueryParam & @FormParam
WebService的參數定義,@QueryParam 通常用在get方法中,@FormParam通常用在Post方法中
package com.qad.yab.ws; import java.util.ArrayList; import javax.ws.rs.Consumes; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import com.google.gson.Gson; import com.qad.yab.dao.CVCUserDAO; import com.qad.yab.dto.User; @Path("/user") public class UserService { @GET @Path("/getUser") @Produces("application/json") public String getUserById(@QueryParam("loginID") String loginID) throws Exception { String users = null; CVCUserDAO dao = new CVCUserDAO(); ArrayList<User> datas = dao.getUsers(loginID); Gson gson = new Gson(); users = gson.toJson(datas); return users; } @POST @Path("/createUser") @Consumes("application/x-www-form-urlencoded") public void createUser(@FormParam("loginID") String loginID, @FormParam("name") String name, @FormParam("email") String email) throws Exception { CVCUserDAO dao = new CVCUserDAO(); dao.createUser(loginID, name, email); } }
沒什麼好解釋的。建立Client,綁定resource,指定QueryParam的參數。若是有多個Query,跟着寫下去就好了,返回結果。
Http 200 code是請求成功的意思
package client; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.WebResource; public class GetUser { public void getUser(String user) { try { Client client = Client.create(); WebResource webResource = client.resource("http://localhost:8080/YAB-CVC-WS/REST/user/getUser?"); ClientResponse response = webResource.queryParam("loginID", user).get(ClientResponse.class); if (response.getStatus() != 200) { throw new RuntimeException("Failed: HTTP error code: " + response.getStatus()); } String output = response.getEntity(String.class); System.out.println(output); } catch (Exception e) { e.printStackTrace(); } } }
和getUser差很少,有兩點值得一提
1. 參數傳遞
與Get不一樣,Post的參數傳遞以Json的數據格式傳入,而經過string定義一個json數據格式顯然太複雜了,這裏用到了官方文檔推薦的MultivaluedMap,具體能夠參見3.5.4. Creating new WebResources from a WebResource
2. 若是post方法是void,Http會返回204錯誤,204錯誤是沒有return content的意思,加個return值就好
package client; import javax.ws.rs.core.MultivaluedMap; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.core.util.MultivaluedMapImpl; public class PostUser { public void createUser(String loginID, String name, String email) { try { Client client = Client.create(); WebResource webResource = client.resource("http://localhost:8080/YAB-CVC-WS/REST/user/createUser?"); MultivaluedMap<String, String> postBody = new MultivaluedMapImpl(); postBody.add("loginID", loginID); postBody.add("name", name); postBody.add("email", email); ClientResponse response = webResource.type("application/x-www-form-urlencoded").post(ClientResponse.class, postBody); if (response.getStatus() != 200 && response.getEntity(String.class) == "Success") { throw new RuntimeException("Failed: HTTP error code: " + response.getStatus()); } System.out.println("User created Successfully"); } catch (Exception e) { e.printStackTrace(); } } }
特別須要提醒的是servlet class,使用不一樣版本的jar包所用的class是不一樣的T_T,深受其害的飄過
http://localhost:8080/YAB-CVC-WS/REST/user/getUser
YAB-CVC-WS:項目名
REST:URL-PATTERN
user: 在WS類中有定義,索引到類
getUser: 在WS類中有定義,索引到方法
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>YAB-CVC-WS</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> <servlet> <servlet-name>ServletService</servlet-name> <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>ServletService</servlet-name> <url-pattern>/REST/*</url-pattern> </servlet-mapping> </web-app>
筆者是第一次利用Jersey開發WebService,也是第一次deploy一個項目到Tomcat,有一些體驗值得記得
1. 環境的安裝
不管是Tomcat,Mysql,都須要進行環境的配置,想尋求一鍵式的解決,可能有,但大多狀況下仍是得本身照着教程一步一步慢慢來,不然偷懶實際上是浪費時間,由於根本無助於解決問題
2. 信心
第一次運行,意味着不知道結果會是怎麼樣的,意味着不知道錯誤是因爲本身代碼寫錯了,仍是系統出錯了(例如Tomcat的Deploy延遲,有時須要deploy兩次war file),大量的時間會耗費在debug上,這些時間多是低效率的,重複的,回頭看若是有一個計劃或者方法來執行試錯,可能更有效率。
3. 可能的提升
Jersey目前已經發布了2.x版本,相比1.x其方法和結構都發生了變化,腦補它應該更加方便使用,但因爲筆者當初先看了1.x的教程,也就沒有再去用2.x,之後有機會嘗試。
沒有在linux上進行deploy和真實環境的測試是另外一個遺憾,畢竟這是公司的research project,之後有時間再補上吧。
Github: https://github.com/Ray-Young/Jersey-WS
以上
引用:
1. Server的實現: http://www.dineshonjava.com/2013/06/restful-web-services-with-jersey-jax-rs.html#.VxCy7Pl96Ul
2. Client的實現:
a. http://stackoverflow.com/questions/13750010/jersey-client-how-to-add-a-list-as-query-parameter (主要用的是這篇)
b. http://www.mkyong.com/webservices/jax-rs/restful-java-client-with-jersey-client/ (這篇更系統)