利用GAE構建第一個REST風格的java webservice
- 配置好你的Eclipse GAE開發環境
- 測試一下你的GAE環境的配置是否正確。寫個HeloWord發佈一下試試。記得翻牆。不翻牆你是發佈不了的。
- 讓我們開始
- 利用GAE建一個gaeRest項目.記得把use google web toolkit勾去
項目如下
注意,droidinvokeRest是客戶端調用的例子,現在先不管。
下載RestEasy 框架http://sourceforge.net/projects/resteasy/files/Resteasy%20JAX-RS/注意,2.2.2.GA該版本在使用過程時會出現java.lang.IllegalAccessException: Reflection is not allowed on protected final java.lang.Class 錯誤。我使用了2.0.0.GA,雖然版本比較老,但是無論是本地發佈還是正式發佈都沒有問題。爲此問題,我也糾結了許久。最終在google app engine 官方開發論壇上找到答案。
下載後,將裏邊的所有jar包拷貝到當前項目的war\web-inf\lib文件中(記得不這樣做,會運行會報錯。。),然後創建userlibray,鏈接到該lib路徑,添加這些包的引用。
創建數據實體,Book類,同時使用jaxb 註解,以用於對象序列化爲 xml。關於jaxb你可以在網上找到相關資料。
代碼如下
package henu.cjt.webservice;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "book")
public class Book {
private String name;
private String content;
public Book() {
}
public Book(String name, String content) {
this.name = name;
this.content = content;
}
@XmlElement
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@XmlElement
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
創建服務類Library,裏邊用來處理客戶端的請求。
使用@Path標註來標識資源路徑。請求該路徑時就會執行該方法。
代碼如下
package henu.cjt.webservice;
import javax.ws.rs.*;
import java.util.*;
import javax.ws.rs.core.*;
@Path("/library")
public class Library {
/**
* 利用books來模擬數據存儲區
* 在構造函數中添加數據
*/
public static List<Book> books = new ArrayList<Book>();
static {
books.add(new Book("huhu", "huhu"));
books.add(new Book("haha", "haha"));
}
/**
* 獲得所有的book列表
* @return
*/
@GET
@Produces({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON })//設定返回的數據類型 xml 格式和json格式
@Path("/books") //資源的相對路徑
public List<Book> listBooks() {
return books;
}
/**
* 獲得指定id的書籍信息
* @param id
* @return
*/
@GET
@Produces({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON })
@Path("/book/{id}")
public Book getBook(@PathParam("id") String id) { //將傳入的id賦值給di
if ("1".equals(id))
return new Book("huhu", "huhu");
else
return new Book("haha", "haha");
}
/**
* 利用put提交方式進行數據更新
* @param book
*/
@PUT
@Path("/book/{name}")
public void updateBook(@PathParam("name") PathSegment book) {
Iterator<Book> it = books.iterator();
String name = String.valueOf(book.getMatrixParameters().get("name"));
String content = String.valueOf(book.getMatrixParameters().get(
"content"));
while (it.hasNext()) {
Book booktmp = it.next();
if (name.equals(booktmp.getName())) {
booktmp.setContent(content);
break;
}
}
}
/**
* 利用post方式進行數據增加
* @param name
* @param content
*/
@POST
@Consumes("application/x-www-form-urlencoded")
@Path("/book/{name}")
public void addBook(@PathParam("name") String name,
@FormParam("content") String content) {
books.add(new Book(name, content));
}
@DELETE
@Path("/book/{name}")
public void deleteBookByName(@PathParam("name") String name){
Iterator<Book> iterator=books.iterator();
while(iterator.hasNext()){
Book book=iterator.next();
if(name.equals(name)){
iterator.remove();
}
}
}
}
創建Application,類名 EasyRestApplication告知有哪些應用。繼承與javax.ws.rs.core.Application
代碼如下
package henu.cjt.webservice;
import java.util.*;
import javax.ws.rs.core.Application;
import org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap;
public class EasyRestApplication extends Application {
HashSet<Object> singletons = new HashSet<Object>();
HashSet<Class<?>> set = new HashSet<Class<?>>();
public EasyRestApplication() {
singletons.add(new Library());//單件模式,客戶端公用一個線程,一個服務實例
// set.add(Library.class);//每個請求出於獨立的線程中,有獨立的實例存在。
}
@Override
public Set<Class<?>> getClasses() {
return set;
}
@Override
public Set<Object> getSingletons() {
return singletons;
}
}
接下來還要配置war文件夾中的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"
version="2.5">
<display-name>gaerest</display-name>
<context-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>henu.cjt.webservice.EasyRestApplication</param-value>
</context-param>
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/gaerest</param-value>
</context-param>
<listener>
<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<servlet>
<servlet-name>GaeRest</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>GaeRest</servlet-name>
<url-pattern>/gaerest/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
上述工作完成後,就可以點擊運行了。然後在瀏覽器中輸入地址(如果你使用代理上網,你可能接受不到數據。。把代理去掉。)
http://localhost:8888/gaerest/library/books
你可以接受到如下內容
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><collection><book><content>huhu</content><name>huhu</name></book><book><content>haha</content><name>haha</name></book></collection>
本地發佈成功。
- 正式發佈
在https://appengine.google.com/登錄後,創建相應的應用後。 然後點擊Eclipse中的GAE發佈按鈕
如果沒有登錄google 賬戶,會提示你登陸接下來填寫project名字時候,記得填寫名字需要是你在google上創建的應用的名字而且,改名字你需要配置在 appengine-web-xml中, <application>cjtmobiles</application>
雖然,這一點已經超出了本文章的講述範圍,但是我還是願意你能夠順利的發佈。
最後提醒,發佈需要翻牆。感謝偉大的牆。
發佈成功後,你可以訪問http://cjtmobiles.appspot.com/gaerest/library/books ,
當然,cjtmobiles是我創建的應用的名字,記得改成你的。
查看結果如果出現500內部server錯誤,你可以通過點擊查看相應的日誌
如果出現如下錯誤
Caused by: java.lang.IllegalAccessException: Reflection is not allowed on protected final java.lang.Class java.lang.ClassLoader.findLoadedClass(java.lang.String)
那麼就是上邊我說的RestEasy版本的問題,其實是其中jaxb的版本問題。
我用jaxb 2.1.12也就是resteasy2.0.0 解決了這個問題。
訪問http://cjtmobiles.appspot.com/gaerest/library/books 成功返回相應數據後,恭喜你,你的服務端模型算是完成了
你也可使用cURL簡單測試restful web service來使用各種提交方式,進行測試。
接下來,我們可以寫一個簡單的android客戶端,模擬調用。