RESTful 的核心就是制定客戶端與服務器交互的統一接口,HttpServlet 基於 HTTP ,可以使用 doXXX 方法來定義CRUD方法。
下面這個栗子,使用了HttpServlet 來實現 RESTful 風格的設計,用doGet 定義查詢接口,用doPost 定義新建接口,用 doPut 定義更新接口,用doDelete 定義刪除接口。Resource representation 格式使用 JSON .
- domain ,一個類,Prediction,who predicts what .
package com.cherry.example.domain;
import java.io.Serializable;
public class Prediction implements Serializable, Comparable<Prediction> {
private String who; // person
private String what; // his/her prediction
private int id; // identifier used as lookup key
public Prediction() {
}
public void setWho(String who) {
this.who = who;
}
public String getWho() {
return this.who;
}
public void setWhat(String what) {
this.what = what;
}
public String getWhat() {
return this.what;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
public int compareTo(Prediction other) {
return this.id - other.id;
}
}
- repository ,不用數據庫,就用一個Map簡單表述
package com.cherry.example.repository;
import java.beans.XMLEncoder;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletContext;
import com.cherry.example.domain.Prediction;
public class Predictions {
private ConcurrentMap<Integer, Prediction> predictions;
private ServletContext sctx;
private AtomicInteger mapKey;
public Predictions() {
predictions = new ConcurrentHashMap<Integer, Prediction>();
mapKey = new AtomicInteger();
}
public void setServletContext(ServletContext sctx) {
this.sctx = sctx;
}
public ServletContext getServletContext() {
return this.sctx;
}
public void setMap(ConcurrentMap<String, Prediction> predictions) {
// no-op for now
}
public ConcurrentMap<Integer, Prediction> getMap() {
// Has the ServletContext been set?
if (getServletContext() == null)
return null;
// Has the data been read already?
if (predictions.size() < 1)
populate();
return this.predictions;
}
public int addPrediction(Prediction p) {
int id = mapKey.incrementAndGet();
p.setId(id);
predictions.put(id, p);
return id;
}
private void populate() {
String filename = "/WEB-INF/data/predictions.db";
InputStream in = sctx.getResourceAsStream(filename);
// Read the data into the array of Predictions.
if (in != null) {
try {
InputStreamReader isr = new InputStreamReader(in);
BufferedReader reader = new BufferedReader(isr);
String record = null;
while ((record = reader.readLine()) != null) {
String[] parts = record.split("!");
Prediction p = new Prediction();
p.setWho(parts[0]);
p.setWhat(parts[1]);
addPrediction(p);
}
} catch (IOException e) {
}
}
}
}
- web ,一個 Servlet ,定義了CRUD接口
package com.cherry.example.web;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.concurrent.ConcurrentMap;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.http.HTTPException;
import com.cherry.example.domain.Prediction;
import com.cherry.example.repository.Predictions;
import com.google.gson.Gson;
@WebServlet(urlPatterns = "/predictions")
public class PredictionsServlet extends HttpServlet {
private Predictions predictions; // backend bean
// Executed when servlet is first loaded into container.
// Create a Predictions object and set its servletContext
// property so that the object can do I/O.
@Override
public void init() {
predictions = new Predictions();
predictions.setServletContext(this.getServletContext());
}
// GET /predictions
// GET /predictions?id=1
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
String result = null;
Gson gson = new Gson();
String param = request.getParameter("id");
Integer key = (param == null) ? null : new Integer(param.trim());
// If no query string, assume client wants the full list.
if (key == null) {
ConcurrentMap<Integer, Prediction> map = predictions.getMap();
// Sort the map's values for readability.
Object[] list = map.values().toArray();
Arrays.sort(list);
result = gson.toJson(list);
}
// Otherwise, return the specified Prediction.
else {
Prediction pred = predictions.getMap().get(key);
if (pred == null) { // no such Prediction
String msg = key + " does not map to a prediction.\n";
result = gson.toJson(msg);
} else { // requested Prediction found
result = gson.toJson(pred);
}
}
// response
sendResponse(response, result);
}
// POST /predictions
// HTTP body should contain two keys, one for the predictor ("who") and
// another for the prediction ("what").
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) {
String who = request.getParameter("who");
String what = request.getParameter("what");
// Are the data to create a new prediction present?
if (who == null || what == null)
throw new HTTPException(HttpServletResponse.SC_BAD_REQUEST);
// Create a Prediction.
Prediction p = new Prediction();
p.setWho(who);
p.setWhat(what);
// Save the ID of the newly created Prediction.
int id = predictions.addPrediction(p);
// Generate the confirmation message.
String msg = "Prediction " + id + " created.\n";
sendResponse(response, msg);
}
// PUT /predictions
// HTTP body should contain at least two keys: the prediction's id
// and either who or what.
@Override
public void doPut(HttpServletRequest request, HttpServletResponse response) {
/*
* A workaround is necessary for a PUT request because neither Tomcat nor Jetty
* generates a workable parameter map for this HTTP verb.
*/
String key = null;
String rest = null;
boolean who = false;
/* Let the hack begin. */
try {
BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
String data = br.readLine();
/*
* To simplify the hack, assume that the PUT request has exactly two parameters:
* the id and either who or what. Assume, further, that the id comes first. From
* the client side, a hash character # separates the id and the who/what, e.g.,
* id=33#who=Homer Allision
*/
String[] args = data.split("#"); // id in args[0], rest in args[1]
String[] parts1 = args[0].split("="); // id = parts1[1]
key = parts1[1];
String[] parts2 = args[1].split("="); // parts2[0] is key
if (parts2[0].contains("who"))
who = true;
rest = parts2[1];
} catch (Exception e) {
throw new HTTPException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
// If no key, then the request is ill formed.
if (key == null)
throw new HTTPException(HttpServletResponse.SC_BAD_REQUEST);
// Look up the specified prediction.
Prediction p = predictions.getMap().get(new Integer(key.trim()));
if (p == null) { // not found?
String msg = key + " does not map to a Prediction.\n";
sendResponse(response, msg);
} else { // found
if (rest == null) {
throw new HTTPException(HttpServletResponse.SC_BAD_REQUEST);
}
// Do the editing.
else {
if (who)
p.setWho(rest);
else
p.setWhat(rest);
String msg = "Prediction " + key + " has been edited.\n";
sendResponse(response, msg);
}
}
}
// DELETE /predictions?id=1
@Override
public void doDelete(HttpServletRequest request, HttpServletResponse response) {
String param = request.getParameter("id");
Integer key = (param == null) ? null : new Integer(param.trim());
// Only one Prediction can be deleted at a time.
if (key == null)
throw new HTTPException(HttpServletResponse.SC_BAD_REQUEST);
try {
predictions.getMap().remove(key);
String msg = "Prediction " + key + " removed.\n";
sendResponse(response, msg);
} catch (Exception e) {
throw new HTTPException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
// Method Not Allowed
@Override
public void doTrace(HttpServletRequest request, HttpServletResponse response) {
throw new HTTPException(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
@Override
public void doHead(HttpServletRequest request, HttpServletResponse response) {
throw new HTTPException(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
@Override
public void doOptions(HttpServletRequest request, HttpServletResponse response) {
throw new HTTPException(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
// Send the response payload to the client.
private void sendResponse(HttpServletResponse response, String payload) {
try {
OutputStream out = response.getOutputStream();
out.write(payload.getBytes());
out.flush();
} catch (Exception e) {
throw new HTTPException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
}