REST(Representational State Transfer)由 Roy Fielding 於 2000 年博士論文提出,核心概念是資源,以 URI 識別,透過 HTTP 方法(GET/POST/PUT/DELETE)操作。RESTful Web 服務即基於 HTTP 與 REST 原理實作的服務。本文以 Jersey(JAX-RS 參考實作)為主,示範從環境建置、Hello World、進階資源設計、多重表現形式(XML/JSON)、JAXB 整合、到客戶端呼叫的完整流程。
jersey-core.jar、jersey-server.jar、jsr311-api.jar、asm.jar(核心);jersey-client.jar(測試);jaxb-impl.jar 等(JAXB/JSON 進階範例需)將 JAR 複製至 WEB-INF/lib,於 web.xml 定義 Jersey Servlet:
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>sample.hello.resources</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
@Path("/hello")
public class HelloResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String sayHello() {
return "Hello Jersey";
}
}
瀏覽器訪問 https://localhost:8080/Jersey/rest/hello 即見「Hello Jersey」。關鍵註解:
@Path:定義資源基礎 URI。@GET:對應 HTTP GET。@Produces:指定回應 MIME 類型。ContactsResource 對應 /contacts,回傳聯絡人清單(XML/JSON)。ContactResource 對應 /contacts/{contactId},處理單一聯絡人 CRUD。@Path("/contacts")
public class ContactsResource {
@Context UriInfo uriInfo;
@Context Request request;
@GET
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public List getContacts() {
List contacts = new ArrayList<>();
contacts.addAll(ContactStore.getStore().values());
return contacts;
}
@Path("{contact}")
public ContactResource getContact(@PathParam("contact") String contact) {
return new ContactResource(uriInfo, request, contact);
}
}
重點註解:
@Context:注入 UriInfo、Request 等上下文物件。@Path("{contact}") + @PathParam("contact"):子資源 URI 參數綁定。@Produces 多重 MIME:APPLICATION_XML 為預設。public class ContactResource {
@Context UriInfo uriInfo;
@Context Request request;
String contact;
public ContactResource(UriInfo uriInfo, Request request, String contact) {
this.uriInfo = uriInfo; this.request = request; this.contact = contact;
}
@GET
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Contact getContact() {
Contact cont = ContactStore.getStore().get(contact);
if (cont == null) throw new NotFoundException("No such Contact.");
return cont;
}
}
| HTTP 方法 | 語意 |
|---|---|
| GET | 獲取/列出/檢索單一或集合資源 |
| POST | 建立新資源(ID 自動指派) |
| PUT | 更新既有資源或集合(亦可建立) |
| DELETE | 刪除資源或集合 |
@POST
@Produces(MediaType.TEXT_HTML)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void newContact(
@FormParam("id") String id,
@FormParam("name") String name,
@Context HttpServletResponse servletResponse) throws IOException {
Contact c = new Contact(id, name, new ArrayList());
ContactStore.getStore().put(id, c);
URI uri = uriInfo.getAbsolutePathBuilder().path(id).build();
Response.created(uri).build();
servletResponse.sendRedirect("../pages/new_contact.html");
}
@PUT
@Consumes(MediaType.APPLICATION_XML)
public Response putContact(JAXBElement jaxbContact) {
Contact c = jaxbContact.getValue();
return putAndGetResponse(c);
}
private Response putAndGetResponse(Contact c) {
Response res;
if (ContactStore.getStore().containsKey(c.getId())) {
res = Response.noContent().build(); // 204
} else {
res = Response.created(uriInfo.getAbsolutePath()).build(); // 201
}
ContactStore.getStore().put(c.getId(), c);
return res;
}
@DELETE
public void deleteContact() {
Contact c = ContactStore.getStore().remove(contact);
if (c == null) throw new NotFoundException("No such Contact.");
}
JAX-RS 支援多種回傳型別:String、Response、void(204)、Resource Class、@XmlRootElement 標註的 POJO(JAXB Bean)、集合等。
@XmlRootElement
public class Contact {
private String id;
private String name;
private List addresses;
// 建構子、getter/setter...
@XmlElement(name="address")
public List getAddresses() { return addresses; }
}
XML:
Shanghai Long Hua Street
Shanghai Dong Quan Street
huangyim
Huang Yi Ming
JSON:
{"contact":[{"address":[{"city":"Shanghai","street":"Long Hua Street"},
{"city":"Shanghai","street":"Dong Quan Street"}],"id":"huangyim","name":"Huang Yi Ming"}]}
# 取得所有聯絡人(XML)
curl https://localhost:8080/Jersey/rest/contacts
# 取得 JSON
curl -H "Accept:application/json" https://localhost:8080/Jersey/rest/contacts
# PUT 新增聯絡人(XML 輸入)
curl -X PUT -H "Content-type:application/xml" \
--data "foo bar " \
https://localhost:8080/Jersey/rest/contacts/foo
Client c = Client.create();
WebResource r = c.resource("https://localhost:8080/Jersey/rest/contacts");
// GET 所有
ClientResponse resp = r.get(ClientResponse.class);
System.out.println(resp.getStatus());
System.out.println(resp.getEntity(String.class));
// PUT 建立
Contact c = new Contact("foo", "Foo Bar", Arrays.asList(new Address("Shanghai", "Ke Yuan Street")));
ClientResponse resp = r.path(c.getId())
.accept(MediaType.APPLICATION_XML)
.put(ClientResponse.class, c);
// GET 單一 + DELETE
GenericType> generic = new GenericType>() {};
JAXBElement jaxb = r.path("foo").type(MediaType.APPLICATION_XML).get(generic);
Contact contact = jaxb.getValue();
r.path("foo").delete(ClientResponse.class);
WebResource 以鏈式 API 建構 URI、設定 Header、送出請求,物件自動綁定 XML/JSON。
Jersey 以標準 JAX-RS 註解大幅簡化 RESTful 服務開發:POJO 資源類、聲明式 HTTP 方法對應、內建多重表現形式序列化、強大客戶端 API。配合 Tomcat 輕量部署,快速建構企業級 REST 服務。