Chào mừng bạn đến với bài hướng dẫn chi tiết về cách xây dựng RESTful web service trong Java. Chúng ta sẽ cùng tìm hiểu về REST (từ viết tắt của REpresentational State Transfer), một kiểu kiến trúc phổ biến được Roy Fielding giới thiệu năm 2000 để phát triển các ứng dụng có khả năng tương tác qua mạng. Bài viết cũng giới thiệu qua các ví dụ tạo các ứng dụng đó với các công cụ REST thông dụng trong Java.
Web service dạng RESTful
Web service dạng RESTful là một kiến trúc client-server (máy khách-máy chủ) phi trạng thái (stateless), trong đó các web service được xem là tài nguyên (resource) và có thể được định danh bằng URI của chúng. Các ứng dụng REST client có thể sử dụng các phương thức HTTP GET/POST để gọi đến các RESTful web service này.
Tuy REST không quy định một giao thức cụ thể nào, trong hầu hết các trường hợp, nó được sử dụng qua HTTP/HTTPS. So với các web service kiểu SOAP, REST nhẹ hơn và không bị ràng buộc bởi một bộ tiêu chuẩn cố định. Chúng ta có thể sử dụng XML, JSON, text hoặc bất kỳ loại dữ liệu nào khác cho request và response.
JAX-RS
JAX-RS (viết tắt của Java API for RESTful Web Services) là bộ API dùng để tạo các REST web service trong Java. Nó sử dụng các annotation để đơn giản hóa việc phát triển và triển khai web service. JAX-RS là một phần của JDK, vì vậy ta không cần thêm bất kỳ thư viện nào để sử dụng các annotation của nó.
Các Annotation của RESTful Web service
Một số annotation quan trọng của JAX-RS bao gồm:
@Path
: Dùng để chỉ định đường dẫn tương đối (relative path) của class và method. Ta có thể lấy URI của một web service bằng cách quét giá trị của annotation Path.GET
,@PUT
,@POST
,@DELETE
và@HEAD
: Dùng để chỉ định loại HTTP request cho một method.Produces
,@Consumes
: Dùng để chỉ định loại dữ liệu của request và response.@PathParam
: Dùng để liên kết tham số của phương thức với một giá trị trong đường dẫn (path) thông qua việc xử lý nó.
Web service dạng REST và SOAP
- SOAP là một giao thức (protocol), trong khi REST là một kiểu kiến trúc ứng dụng.
- Các ứng dụng server và client của SOAP được liên kết chặt chẽ và ràng buộc bởi hợp đồng WSDL, trong khi với REST web service và client không có hợp đồng ràng buộc nào.
- Việc học REST dễ dàng hơn so với SOAP.
- Loại dữ liệu request và response của REST có thể là XML, JSON, text v.v., trong khi SOAP chỉ làm việc với XML.
- JAX-RS là API của Java cho REST, còn JAX-WS là API của Java cho web service dạng SOAP.
Các triển khai của REST API
Có hai bản triển khai chính của JAX-RS API:
- Jersey: Đây là bản triển khai tham chiếu được cung cấp bởi Sun. Để sử dụng Jersey làm bản triển khai JAX-RS, tất cả những gì chúng ta cần làm là cấu hình servlet của nó trong
web.xml
và thêm các phụ thuộc cần thiết. Lưu ý rằng JAX-RS API là một phần của JDK chứ không phải của Jersey, vì vậy chúng ta phải thêm các file phụ thuộc jar của nó vào ứng dụng. - RESTEasy: Đây là dự án của JBoss nhằm cung cấp một bản triển khai khác của JAX-RS.
Hướng dẫn tạo web service dạng RESTful với Java
Hãy cùng tạo một RESTful web service bằng Jersey và sau đó là RESTEasy. Chúng ta sẽ cung cấp các phương thức sau qua HTTP và sử dụng extension Postman của Chrome để kiểm thử.
URI | Phương thức HTTP | Mô tả |
---|---|---|
/person/{id}/getDummy | GET | Trả về một đối tượng person mẫu |
/person/add | POST | Thêm một person |
/person/{id}/delete | GET | Xóa person có ‘id’ trong URI |
/person/getAll | GET | Lấy tất cả person |
/person/{id}/get | GET | Lấy person có ‘id’ trong URI |
Web service dạng RESTful với Jersey
Hãy tạo một ứng dụng web động và sau đó chuyển đổi nó thành một project Maven để có được bộ khung cho dự án của chúng ta. Hình dưới đây cho thấy cấu trúc của dự án cuối cùng.
Hãy cùng xem các dependency của Jersey mà chúng ta có trong file pom.xml
.
<project xmlns="<https://maven.apache.org/POM/4.0.0>" xmlns:xsi="<https://www.w3.org/2001/XMLSchema-instance>" xsi:schemaLocation="<https://maven.apache.org/POM/4.0.0> <https://maven.apache.org/xsd/maven-4.0.0.xsd>">
<modelVersion>4.0.0</modelVersion>
<groupId>JAXRS-Example</groupId>
<artifactId>JAXRS-Example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.19</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
<version>1.19</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.19</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Tuy ta không bắt buộc phải thêm các dependency của jersey-client, việc này rất cần thiết nếu bạn đang viết một chương trình Java để gọi một REST web service bằng Jersey. Bây giờ, hãy xem qua deployment descriptor (bộ miêu tả triển khai) ở dưới để tìm hiểu cách cấu hình Jersey nhằm tạo ra ứng dụng web ta cần.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="<https://www.w3.org/2001/XMLSchema-instance>" xmlns="<https://xmlns.jcp.org/xml/ns/javaee>" xsi:schemaLocation="<https://xmlns.jcp.org/xml/ns/javaee> <https://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd>" id="WebApp_ID" version="3.1">
<display-name>JAXRS-Example</display-name>
<!-- Jersey Servlet configurations -->
<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>com.journaldev</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<!-- Jersey Servlet configurations -->
</web-app>
Đó là tất cả những gì cần thiết để tích hợp Jersey vào ứng dụng web của chúng ta. Trong code Java, ta sẽ sử dụng các annotation của JAX-RS. Hãy chú ý đến giá trị của com.sun.jersey.config.property.packages
dùng để cung cấp package sẽ được quét để tìm các resource và method của web service.
Các class Model
Trước hết, chúng ta sẽ tạo hai model bean: Person
cho dữ liệu ứng dụng và Response
để gửi response đến các hệ thống client. Vì chúng ta sẽ gửi response dưới dạng XML, các bean cần được đánh dấu bằng annotation , và ta có class này.
package com.journaldev.jaxrs.model;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement (name="person")
public class Person {
private String name;
private int age;
private int id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString(){
return id+"::"+name+"::"+age;
}
}
package com.journaldev.jaxrs.model;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Response {
private boolean status;
private String message;
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Các Service
Dựa trên cấu trúc URI, ta interface cho service và code triển khai của nó như dưới:
package com.journaldev.jaxrs.service;
import com.journaldev.jaxrs.model.Person;
import com.journaldev.jaxrs.model.Response;
public interface PersonService {
public Response addPerson(Person p);
public Response deletePerson(int id);
public Person getPerson(int id);
public Person[] getAllPersons();
}
package com.journaldev.jaxrs.service;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.journaldev.jaxrs.model.Person;
import com.journaldev.jaxrs.model.Response;
@Path("/person")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public class PersonServiceImpl implements PersonService {
private static Map<Integer,Person> persons = new HashMap<Integer,Person>();
@Override
@POST
@Path("/add")
public Response addPerson(Person p) {
Response response = new Response();
if(persons.get(p.getId()) != null){
response.setStatus(false);
response.setMessage("Person Already Exists");
return response;
}
persons.put(p.getId(), p);
response.setStatus(true);
response.setMessage("Person created successfully");
return response;
}
@Override
@GET
@Path("/{id}/delete")
public Response deletePerson(@PathParam("id") int id) {
Response response = new Response();
if(persons.get(id) == null){
response.setStatus(false);
response.setMessage("Person Doesn't Exists");
return response;
}
persons.remove(id);
response.setStatus(true);
response.setMessage("Person deleted successfully");
return response;
}
@Override
@GET
@Path("/{id}/get")
public Person getPerson(@PathParam("id") int id) {
return persons.get(id);
}
@GET
@Path("/{id}/getDummy")
public Person getDummyPerson(@PathParam("id") int id) {
Person p = new Person();
p.setAge(99);
p.setName("Dummy");
p.setId(id);
return p;
}
@Override
@GET
@Path("/getAll")
public Person[] getAllPersons() {
Set<Integer> ids = persons.keySet();
Person[] p = new Person[ids.size()];
int i=0;
for(Integer id : ids){
p[i] = persons.get(id);
i++;
}
return p;
}
}
Hầu hết đoạn code ở trên khá dễ hiểu. Bạn hãy dành chút thời gian để làm quen với các annotation của JAX-RS như Path
, @PathParam
, @POST
, @GET
, @Consumes
và @Produces
.
Kiểm thử
Bây giờ ta chỉ cần export web service của chúng ta ra file WAR và đặt vào thư mục webapps
của Tomcat (hoặc triển khai trên bất kỳ container nào bạn muốn). Dưới đây là một vài bài test được thực hiện bằng extension Postman của Chrome cho web service này.
Lưu ý rằng chúng ta phải cung cấp giá trị cho Accept
và Content-Type
là “application/xml” trong header của request như hình dưới đây.
getDummy
add
get
getAll
delete
Đó là tất cả các bước cần để tạo web service bằng Jersey. Như bạn có thể thấy, hầu hết code đều sử dụng các annotation của JAX-RS và Jersey được tích hợp thông qua deployment descriptor và các dependency.
RESTful Web service với RESTEasy
Tuy ta sẽ sử dụng lại toàn bộ logic nghiệp vụ được dùng trong ví dụ Jersey ở trên, thay vì chỉnh sửa trên cùng một dự án, ta đã tạo một dự án mới. Hãy tạo một ứng dụng web động và chuyển đổi nó thành dự án Maven. Sau đó, sao chép tất cả các class Java – Person
, Response
, PersonService
và PersonServiceImpl
.
Dưới đây là cấu trúc của dự án sau khi chúng ta hoàn tất mọi thay đổi.
Thêm các phụ thuộc của RESTEasy dưới đây vào file pom.xml
.
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>3.0.13.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>3.0.13.Final</version>
</dependency>
Dưới đây là file web.xml
nơi chúng ta cấu hình servlet của RESTEasy.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="<https://www.w3.org/2001/XMLSchema-instance>" xmlns="<https://xmlns.jcp.org/xml/ns/javaee>" xsi:schemaLocation="<https://xmlns.jcp.org/xml/ns/javaee> <https://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd>" id="WebApp_ID" version="3.1">
<display-name>JAXRS-Example-RestEasy</display-name>
<listener>
<listener-class>
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
</listener-class>
</listener>
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.journaldev.jaxrs.resteasy.app.MyApp</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Hãy chú ý đến init-param
nơi chúng ta cung cấp class MyApp
làm giá trị. Ta kế thừa class javax.ws.rs.core.Application
như dưới đây.
package com.journaldev.jaxrs.resteasy.app;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;
import com.journaldev.jaxrs.service.PersonServiceImpl;
public class MyApp extends Application {
private Set<Object> singletons = new HashSet<Object>();
public MyApp() {
singletons.add(new PersonServiceImpl());
}
@Override
public Set<Object> getSingletons() {
return singletons;
}
}
Kiểm thử
Web service của chúng ta đã sẵn sàng với bản triển khai RESTEasy của JAX-RS. Dưới đây là một số kết quả của việc kiểm thử bằng extension Postman của Chrome.
getDummy
add
get
Tổng kết
Hy vọng qua bài hướng dẫn này bạn đã có được kiến thức toàn diện và sự tự tin để bắt đầu xây dựng các RESTful Web service của riêng mình. Đừng ngần ngại thử nghiệm với các annotation của JAX-RS cũng như trải nghiệm sự linh hoạt khi chuyển đổi giữa Jersey và RESTEasy nhờ các API được cung cấp sẵn.