/books/1)。Accept 或副檔名決定。核心 RestActionMapper 將 Rails 風格 URL 映射至 Action 七大標準方法:
| HTTP 方法 | URI 範例 | Action 方法 | id 參數 |
|---|---|---|---|
| GET | /books | index() | 無 |
| POST | /books | create() | 無 |
| GET | /books/1 | show() | 1 |
| PUT | /books/1 | update() | 1 |
| DELETE | /books/1 | destroy() | 1 |
| GET | /books/1/edit | edit() | 1 |
| GET | /books/new | editNew() | 無 |
HTML 表單不支援 PUT/DELETE,約定加隱藏欄位 _method=put 或 _method=delete 模擬。
WEB-INF/lib:
struts.xml 設定預設父包為 rest-default:<constant name="struts.convention.default.parent.package" value="rest-default"/>
<constant name="struts.convention.action.suffix" value="Controller"/>
<constant name="struts.convention.action.mapAllMatches" value="true"/>
public class Book {
private Integer id; private String name; private double price;
// getter/setter 略
}
public class BookService {
private static Map<Integer, Book> books = new HashMap<>();
private static int nextId = 5;
static { /* 初始 4 筆資料 */ }
public Book get(int id) { return books.get(id); }
public List<Book> getAll() { return new ArrayList<>(books.values()); }
public void saveOrUpdate(Book b) { if (b.getId()==null) b.setId(nextId++); books.put(b.getId(), b); }
public void remove(int id) { books.remove(id); }
}
@Results(@Result(name="success", type="redirectAction", params={"actionName", "book"}))
public class BookController extends ActionSupport implements ModelDriven<Object> {
private int id;
private Book model = new Book();
private List<Book> list;
private BookService bookService = new BookService();
public void setId(int id) {
this.id = id;
if (id > 0) this.model = bookService.get(id); // 自動載入模型
}
public int getId() { return id; }
// 1. GET /books
public HttpHeaders index() {
list = bookService.getAll();
return new DefaultHttpHeaders("index").disableCaching();
}
// 2. GET /books/new
public String editNew() {
model = new Book();
return "editNew";
}
// 3. POST /books
public HttpHeaders create() {
bookService.saveOrUpdate(model);
addActionMessage("新增成功");
return new DefaultHttpHeaders("success").setLocationId(model.getId());
}
// 4. GET /books/1
public HttpHeaders show() {
return new DefaultHttpHeaders("show");
}
// 5. GET /books/1/edit
public String edit() { return "edit"; }
// 6. PUT /books/1
public String update() {
bookService.saveOrUpdate(model);
addActionMessage("更新成功");
return "success";
}
// 7. GET /books/1/deleteConfirm (額外動作)
public String deleteConfirm() { return "deleteConfirm"; }
// 8. DELETE /books/1
public String destroy() {
bookService.remove(id);
addActionMessage("刪除 ID " + id);
return "success";
}
@Override public Object getModel() { return list != null ? list : model; }
}
HttpHeaders 允許設定快取控制、Location 標頭等 REST 元數據。ModelDriven 讓結果類型直接序列化模型物件(XML/JSON)。Convention 預設視圖路徑:WEB-INF/content/{actionName}-{methodName}.jsp。
<s:iterator value="model">
<tr>
<td><s:property value="id"/></td>
<td>${name}</td><td>${price}</td>
<td>
<a href="book/${id}">查看</a> |
<a href="book/${id}/edit">編輯</a> |
<a href="book/${id}/deleteConfirm">刪除</a>
</td>
</tr>
</s:iterator>
<a href="book/new">新增書籍</a>
<s:form method="post" action="%{#request.contextPath}/book/%{id}">
<s:hidden name="_method" value="put" /> <!-- 模擬 PUT -->
<s:textfield name="name" label="書名"/>
<s:textfield name="price" label="價格"/>
<s:submit value="修改"/>
</s:form>
<s:form method="post" action="%{#request.contextPath}/book/%{id}">
<s:hidden name="_method" value="delete" />
確定刪除《${name}》嗎? <s:submit value="刪除"/>
</s:form>
/books → HTML 列表/books.xml → XML 序列化(XStream)/books.json → JSON 序列化(json-lib)Struts 2 REST Plugin 以 Convention over Configuration 方式,將傳統 Action 轉為 RESTful Controller。關鍵在於:rest-default 父包、七大標準方法映射、HttpHeaders 控制回應元數據、_method 參數模擬 PUT/DELETE、ModelDriven 統一序列化入口。掌握此模式,可快速建構符合 REST 原則、支援多重表現的企業級 API 服務。