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); } }}