Nội dung chính
Các công nghệ sử dụng trong ví dụ này
- Java 8
- Spring 4.3.6.RELEASE
- Apache Tomcat 8.5
Cấu trúc Spring project trông như sau:
File pom.xml
<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.springframework.samples.service.service</groupId> <artifactId>spring-ws-example</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <properties> <!-- Generic properties --> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <!-- Web --> <servlet.version>2.5</servlet.version> <!-- Spring --> <spring-framework.version>4.3.6.RELEASE</spring-framework.version> <!-- JSON --> <jackson.version>2.5.3</jackson.version> </properties> <dependencies> <!-- Spring MVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring-framework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring-framework.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <!-- Other Web dependencies --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>${servlet.version}</version> <scope>provided</scope> </dependency> </dependencies> </project>
Cấu hình Spring project
Trong ví dụ này, chúng ta sử dụng @Annotation để cấu hình cho Spring project bằng cách sửa file web.xml và tạo ra lớp cấu hình ApplicationConfig.java có nội dung như sau:
File: ApplicationConfig.java
package vn.kienthuclaptrinh.ws.configuration; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @EnableWebMvc @Configuration @ComponentScan(basePackages = { "vn.kienthuclaptrinh" }) public class ApplicationConfig { }
Trong đó, @ComponentScan(basePackages = { "vn.kienthuclaptrinh" } được sử dụng để quét tất cả các lớp cấu hình trong gói vn.kienthuclaptrinh.
File: web.xml
<?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>spring-ws-example</display-name> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- annotated that this application is config by annotation, instead of default by xml --> <context-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </context-param> <!-- indicate spring annotation configuration class --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>vn.kienthuclaptrinh.ws.configuration.ApplicationConfig</param-value> </context-param> <!-- Servlet that dispatches request to registered handlers (Controller implementations).--> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value></param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
Tạo các lớp model
File: Users.java
package vn.kienthuclaptrinh.ws.model; public class Users { private long id; private String name; private int age; private double salary; public Users() { id = 0; } public Users(long id, String name, int age, double salary) { this.id = id; this.name = name; this.age = age; this.salary = salary; } public long getId() { return id; } public void setId(long id) { this.id = 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 double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } @Override public String toString() { return "User [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]"; } }
File: UserService.java
package vn.kienthuclaptrinh.ws.service; import java.util.List; import vn.kienthuclaptrinh.ws.model.Users; public interface UserService { Users findById(long id); Users findByName(String name); void saveUser(Users user); void updateUser(Users user); void deleteUserById(long id); List<Users> findAllUsers(); void deleteAllUsers(); public boolean isUserExist(Users user); }
File: UserServiceImpl.java
package vn.kienthuclaptrinh.ws.service; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import vn.kienthuclaptrinh.ws.model.Users; @Service("userService") @Transactional public class UserServiceImpl implements UserService { private static final AtomicLong counter = new AtomicLong(); private static List<Users> users; static { users = createDummyUsers(); } public List<Users> findAllUsers() { return users; } public Users findById(long id) { for (Users user : users) { if (user.getId() == id) { return user; } } return null; } public Users findByName(String name) { for (Users user : users) { if (user.getName().equalsIgnoreCase(name)) { return user; } } return null; } public void saveUser(Users user) { user.setId(counter.incrementAndGet()); users.add(user); } public void updateUser(Users user) { int index = users.indexOf(user); users.set(index, user); } public void deleteUserById(long id) { for (Iterator<Users> iterator = users.iterator(); iterator.hasNext();) { Users user = iterator.next(); if (user.getId() == id) { iterator.remove(); } } } public boolean isUserExist(Users user) { return findByName(user.getName()) != null; } public void deleteAllUsers() { users.clear(); } private static List<Users> createDummyUsers() { List<Users> users = new ArrayList<Users>(); users.add(new Users(counter.incrementAndGet(), "Cong", 30, 70000)); users.add(new Users(counter.incrementAndGet(), "Dung", 40, 50000)); users.add(new Users(counter.incrementAndGet(), "Ngon", 45, 30000)); users.add(new Users(counter.incrementAndGet(), "Hanh", 50, 40000)); return users; } }
Tạo Web Service
Trong thiết kế dựa trên REST, khi thao tác với các tài nguyên, chúng ta nên tuân theo một số quy định như sau:
- Để tạo mới tài nguyên, nên sử dụng HTTP POST.
- Để lấy ra tài nguyên, nên sử dụng HTTP GET.
- Để cập nhật tài nguyên, nên sử dụng HTTP PUT.
- Để xóa tài nguyên, nên sử dụng HTTP DELETE.
File: UserRestController.java
package vn.kienthuclaptrinh.ws.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; 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.RestController; import org.springframework.web.util.UriComponentsBuilder; import vn.kienthuclaptrinh.ws.model.Users; import vn.kienthuclaptrinh.ws.service.UserService; @RestController public class UserRestController { @Autowired UserService userService; // Retrieve all users @RequestMapping(value = "/user", method = RequestMethod.GET) public ResponseEntity<List<Users>> listAllUsers() { List<Users> users = userService.findAllUsers(); if (users.isEmpty()) { return new ResponseEntity<List<Users>>(HttpStatus.NO_CONTENT); } return new ResponseEntity<List<Users>>(users, HttpStatus.OK); } // Retrieve user by id @RequestMapping(value = "/user/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<Users> getUser(@PathVariable("id") long id) { System.out.println("Fetching User with id " + id); Users user = userService.findById(id); if (user == null) { System.out.println("User with id " + id + " not found"); return new ResponseEntity<Users>(HttpStatus.NOT_FOUND); } return new ResponseEntity<Users>(user, HttpStatus.OK); } // Create a new user @RequestMapping(value = "/user", method = RequestMethod.POST) public ResponseEntity<Void> createUser(@RequestBody Users user, UriComponentsBuilder ucBuilder) { System.out.println("Creating User " + user.getName()); if (userService.isUserExist(user)) { System.out.println("A User with name " + user.getName() + " already exist"); return new ResponseEntity<Void>(HttpStatus.CONFLICT); } userService.saveUser(user); HttpHeaders headers = new HttpHeaders(); headers.setLocation(ucBuilder.path("/user/{id}").buildAndExpand(user.getId()).toUri()); return new ResponseEntity<Void>(headers, HttpStatus.CREATED); } // Update a User @RequestMapping(value = "/user/{id}", method = RequestMethod.PUT) public ResponseEntity<Users> updateUser(@PathVariable("id") long id, @RequestBody Users user) { System.out.println("Updating User " + id); Users currentUser = userService.findById(id); if (currentUser == null) { System.out.println("User with id " + id + " not found"); return new ResponseEntity<Users>(HttpStatus.NOT_FOUND); } currentUser.setName(user.getName()); currentUser.setAge(user.getAge()); currentUser.setSalary(user.getSalary()); userService.updateUser(currentUser); return new ResponseEntity<Users>(currentUser, HttpStatus.OK); } // Delete a User @RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE) public ResponseEntity<Users> deleteUser(@PathVariable("id") long id) { System.out.println("Fetching & Deleting User with id " + id); Users user = userService.findById(id); if (user == null) { System.out.println("Unable to delete. User with id " + id + " not found"); return new ResponseEntity<Users>(HttpStatus.NOT_FOUND); } userService.deleteUserById(id); return new ResponseEntity<Users>(HttpStatus.NO_CONTENT); } // Delete All User @RequestMapping(value = "/user", method = RequestMethod.DELETE) public ResponseEntity<Users> deleteAllUsers() { System.out.println("Deleting All Users"); userService.deleteAllUsers(); return new ResponseEntity<Users>(HttpStatus.NO_CONTENT); } }
Trong lớp UserRestController.java, chúng ta đã cài đặt các REST API như sau:
- GET yêu cầu url = /user trả về một danh sách users
- GET yêu cầu url = /user/1 trả về một user với ID = 1
- POST yêu cầu url = /user với một đối tượng user ở dạng chuỗi JSON để tạo mới một user
- PUT yêu cầu url = /user/3 với một đối tượng user ở dạng chuỗi JSON để cập nhật một user có ID = 3
- DELETE yêu cầu url = /user/4 xóa user có ID = 4
- DELETE yêu cầu url = /user xóa tất cả users
Chạy ứng dụng và gọi Web Service
Để debug và test một ứng dụng web service trên trình duyệt Firefox, bạn có thể sử dụng plugin RESTClient, link cài đặt https://addons.mozilla.org/vi/firefox/addon/restclient/
Chúng tôi chạy ứng dụng trên port 8081, sau khi chạy ứng dụng có url = "http://localhost:8081/spring-ws-example"
GET list users
Method: GET
URL: http://localhost:8081/spring-ws-example/user
Kết quả:
Create new user
Method: POST
URL: http://localhost:8081/spring-ws-example/user
Headers:
Content-Type: application/json
Kết quả: