Ví dụ CRUD Restful Web Service với Spring Boot

Xem thêm các chuyên mục:

1- Mục tiêu của ví dụ

Tài liệu được viết dựa trên:
  • Spring Boot 2.x

  • Eclipse 4.7 Oxygen

Xem thêm:
Trong bài viết này tôi sẽ hướng dẫn bạn tạo một ứng dụng Restful Web Service sử dụng Spring Boot, và có 4 chức năng Create, Read, Update, Delete (CRUD).

Read (GET method)

Chúng ta sẽ xây dựng một URI, nó được chỉ định (designated) để trả về cho người dùng một danh sách các nhân viên (employee). Và định nghĩa ra một URI khác để trả về cho người dùng thông tin của một nhân viên cụ thể. Dữ liệu người dùng sẽ nhận được theo định dạng XML hoặc JSON. Các URI này chỉ chấp nhận các request với phương thức GET.
  • GET http://localhost:8080/employees
  • GET http://localhost:8080/employee/E01

Update (PUT method).

Xây dựng một URI để xử lý yêu cầu (request) thay đổi thông tin một nhân viên (employee). URI này chỉ chấp nhận request với phương thức PUT. Dữ liệu gửi kèm theo request là thông tin mới của nhân viên, nó có định dạng XML hoặc JSON.
  • PUT http://localhost:8080/employee

Create (POST method)

Xây dựng một URI để xử lý yêu cầu (request) tạo mới một nhân viên (employee). URI này chỉ chấp nhận request với phương thức POST. Dữ liệu được gửi kèm theo yêu cầu là thông tin của nhân viên sẽ được tạo, nó có định dạng XML hoặc JSON.
  • POST http://localhost:8080/employee

Delete (DELETE method).

Xây dựng một URI để xử lý yêu cầu (request) xóa một nhân viên (employee). URI này chỉ chấp nhận request với phương thức DELETE.
Chú ý: Không có dữ liệu nào được đính kèm theo request trong trường hợp này (Giống như dữ liệu đính kèm theo phương thức POST), vì request với phương thức DELETE không thể đính kèm theo dữ liệu. Thông tin nhân viên cần xóa sẽ nằm trên URI hoặc nằm trên QueryString của URL.
  • DELETE  http://localhost:8080/employee/{empNo}

2- Tạo Spring Boot project

Trên Eclipse chọn:
  • File/New/Other..
Nhập vào:
  • Name: SpringBootCrudRestful
  • Group: org.o7planning
  • Package: org.o7planning.sbcrudrestful
Bước tiếp theo bạn cần lựa chọn các công nghệ sẽ sử dụng.
OK, Project đã được tạo ra.

3- Cấu hình pom.xml

Trong ví dụ này chúng ta cần một thư viện để chuyển đổi (convert) XML sang đối tượng Java và ngược lại. Và một thư viện khác để chuyển đổi JSON sang Java và ngược lại.

JSON <==> Java

spring-boot-starter-web đã tích hợp sẵn thư viện jackson-databind, thư viện này giúp chuyển đổi JSON thành đối tượng Java và ngược lại.
 

XML <==> Java

Spring Boot sử dụng JAXB (Có sẵn trong JDK) như là một thư viện mặc định để chuyển đổi XMLJava. Tuy nhiên các lớp Java của bạn cần phải được chú thích (annotated) bởi @XmlRootElement,... Vì vậy lời khuyên của tôi là bạn nên sử dụng jackson-dataformat-xml như là một thư viện để chuyển đổi XMLJava. Để sử dụng jackson-dataformat-xml bạn cần khai báo nó trong tập tin pom.xml:
** pom.xml **
...

<dependencies>

        ...
    
    <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-xml</artifactId>
    </dependency>

    ...

</dependencies>

...
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                        http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.o7planning</groupId>
    <artifactId>SpringBootCrudRestful</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>SpringBootCrudRestful</name>
    <description>Spring Boot + Restful</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

4- Code của ứng dụng

SpringBootCrudRestfulApplication.java
package org.o7planning.sbcrudrestful;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootCrudRestfulApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootCrudRestfulApplication.class, args);
    }
}

 
Lớp Employee đại diện cho một nhân viên.
Employee.java
package org.o7planning.sbcrudrestful.model;

public class Employee {

    private String empNo;
    private String empName;
    private String position;

    public Employee() {

    }

    public Employee(String empNo, String empName, String position) {
        this.empNo = empNo;
        this.empName = empName;
        this.position = position;
    }

    public String getEmpNo() {
        return empNo;
    }

    public void setEmpNo(String empNo) {
        this.empNo = empNo;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public String getPosition() {
        return position;
    }

    public void setPosition(String position) {
        this.position = position;
    }

}
Lớp EmployeeDAO được chú thích (annotate) bởi @Repository để thông báo với Spring rằng nó là một Spring BEAN. Lớp này bao gồm các phương thức giúp truy vấn danh sách các nhân viên (employee), tạo nhân viên, sửa đổi thông tin nhân viên, và xóa nhân viên.
EmployeeDAO.java
package org.o7planning.sbcrudrestful.dao;
 

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.o7planning.sbcrudrestful.model.Employee;
import org.springframework.stereotype.Repository;

@Repository
public class EmployeeDAO {

    private static final Map<String, Employee> empMap = new HashMap<String, Employee>();

    static {
        initEmps();
    }

    private static void initEmps() {
        Employee emp1 = new Employee("E01", "Smith", "Clerk");
        Employee emp2 = new Employee("E02", "Allen", "Salesman");
        Employee emp3 = new Employee("E03", "Jones", "Manager");

        empMap.put(emp1.getEmpNo(), emp1);
        empMap.put(emp2.getEmpNo(), emp2);
        empMap.put(emp3.getEmpNo(), emp3);
    }

    public Employee getEmployee(String empNo) {
        return empMap.get(empNo);
    }

    public Employee addEmployee(Employee emp) {
        empMap.put(emp.getEmpNo(), emp);
        return emp;
    }

    public Employee updateEmployee(Employee emp) {
        empMap.put(emp.getEmpNo(), emp);
        return emp;
    }

    public void deleteEmployee(String empNo) {
        empMap.remove(empNo);
    }

    public List<Employee> getAllEmployees() {
        Collection<Employee> c = empMap.values();
        List<Employee> list = new ArrayList<Employee>();
        list.addAll(c);
        return list;
    }

}
Lớp MainRESTController được chú thích (annotate) bởi @RestController để thông báo với Spring rằng nó là một Spring Restful Controller,
MainRESTController.java
package org.o7planning.sbcrudrestful.controller;

import java.util.List;

import org.o7planning.sbcrudrestful.dao.EmployeeDAO;
import org.o7planning.sbcrudrestful.model.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController

public class MainRESTController {

    @Autowired
    private EmployeeDAO employeeDAO;

    @RequestMapping("/")
    @ResponseBody
    public String welcome() {
        return "Welcome to RestTemplate Example.";
    }

    // URL:
    // http://localhost:8080/SomeContextPath/employees
    // http://localhost:8080/SomeContextPath/employees.xml
    // http://localhost:8080/SomeContextPath/employees.json
    @RequestMapping(value = "/employees", //
            method = RequestMethod.GET, //
            produces = { MediaType.APPLICATION_JSON_VALUE, //
                    MediaType.APPLICATION_XML_VALUE })
    @ResponseBody
    public List<Employee> getEmployees() {
        List<Employee> list = employeeDAO.getAllEmployees();
        return list;
    }

    // URL:
    // http://localhost:8080/SomeContextPath/employee/{empNo}
    // http://localhost:8080/SomeContextPath/employee/{empNo}.xml
    // http://localhost:8080/SomeContextPath/employee/{empNo}.json
    @RequestMapping(value = "/employee/{empNo}", //
            method = RequestMethod.GET, //
            produces = { MediaType.APPLICATION_JSON_VALUE, //
                    MediaType.APPLICATION_XML_VALUE })
    @ResponseBody
    public Employee getEmployee(@PathVariable("empNo") String empNo) {
        return employeeDAO.getEmployee(empNo);
    }

    // URL:
    // http://localhost:8080/SomeContextPath/employee
    // http://localhost:8080/SomeContextPath/employee.xml
    // http://localhost:8080/SomeContextPath/employee.json

    @RequestMapping(value = "/employee", //
            method = RequestMethod.POST, //
            produces = { MediaType.APPLICATION_JSON_VALUE, //
                    MediaType.APPLICATION_XML_VALUE })
    @ResponseBody
    public Employee addEmployee(@RequestBody Employee emp) {

        System.out.println("(Service Side) Creating employee: " + emp.getEmpNo());

        return employeeDAO.addEmployee(emp);
    }

    // URL:
    // http://localhost:8080/SomeContextPath/employee
    // http://localhost:8080/SomeContextPath/employee.xml
    // http://localhost:8080/SomeContextPath/employee.json
    @RequestMapping(value = "/employee", //
            method = RequestMethod.PUT, //
            produces = { MediaType.APPLICATION_JSON_VALUE, //
                    MediaType.APPLICATION_XML_VALUE })
    @ResponseBody
    public Employee updateEmployee(@RequestBody Employee emp) {

        System.out.println("(Service Side) Editing employee: " + emp.getEmpNo());

        return employeeDAO.updateEmployee(emp);
    }

    // URL:
    // http://localhost:8080/SomeContextPath/employee/{empNo}
    @RequestMapping(value = "/employee/{empNo}", //
            method = RequestMethod.DELETE, //
            produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
    @ResponseBody
    public void deleteEmployee(@PathVariable("empNo") String empNo) {

        System.out.println("(Service Side) Deleting employee: " + empNo);

        employeeDAO.deleteEmployee(empNo);
    }

}

Giải thích:

  • produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE }
  • produces = { "application/json" , "application/xml" }
Thuộc tính produces được sử dụng để quy định một URL sẽ chỉ tạo ra (trả về cho người dùng) các dữ liệu với các định dạng nào. Chẳng hạn "application/json", "application/xml".

5- Chạy ứng dụng

Để chạy ứng dụng, nhấn phải chuột vào Project chọn:
  • Run As/Spring Boot App
Sau khi chạy ứng dụng, bạn có thể test các chức năng của nó.

Test GET:

If ERROR==> Please use Spring Boot 2.0.0.M5 temporarily.(And wait for the official version of Spring Boot 2).
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.M5</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
Để lấy danh sách các nhân viên (employee) người dùng cần gửi một request (yêu cầu) với phương thức GET. Bạn có thể dễ dàng test chức năng này bằng cách sử dụng trình duyệt.
Hoặc:

Làm sao để tạo một request sử dụng phương thức POST, PUT hoặc DELETE?

Để tạo một request (yêu cầu) với phương thức POST, PUT hoặc DELETE bạn phải sử dụng một công cụ chẳng hạn như RestClient, cURL,.. hoặc viết một ứng dụng Rest Client của riêng bạn.
Xem thêm:

Test POST

Để tạo một nhân viên (employee), bạn cần tạo một request với phương thức POST, và gửi kèm theo thông tin nhân viên sẽ được tạo ra, dữ liệu gửi đi có định dạng JSON hoặc XML:
POST http://localhost:8080/employee
Acept: application/xml

<Employee>
    <empNo>E11</empNo>
    <empName>New Employee</empName>
    <position>Clerk</position>
</Employee>
POST http://localhost:8080/employee
Acept: application/json

{"empNo":"E11","empName":"New Employee","position":"Clerk"}

Xem thêm các chuyên mục: