在這篇文章中,我們將使用Spring4 MVC編寫一個CRUD RESTful Web服務,寫一個REST客戶端RestTemplate來使用這些服務。我們也將利用外部客戶端測試的服務。
下面將展示核心代碼,更詳細的代碼實現參照Maven示例工程!
簡單介紹REST
REST表示狀態傳輸。這是一個體系結構樣式,可用於設計網絡服務,可以被各種客戶端消耗。核心思想是,不使用如CORBA,RPC或SOAP復雜的機制在機器之間進行連接,簡單的 HTTP 用於使它們之間調用。
-
創建資源:應該使用 HTTP POST
-
要獲取資源:應該使用HTTP GET
-
更新資源:應使用HTTP PUT
-
要刪除資源:應使用HTTP DELETE
通常REST是基於Web服務返回JSON或XML數據格式作為響應,雖然它並不僅僅限於這些類型。客戶端可以指定(使用 HTTP Accept 報頭),他們所感興趣的資源類型,並且服務器可以返回資源,指定它所服務的內容類型資源。
下面是一個可能基於REST的控制器,實現REST API。這里所說的“可能”,這意味着可以以另一種方式實現它,還是(或者更純粹的方式)符合REST風格。
- GET請求/api/user/返回用戶的列表
- GET請求/api/user/1返回ID為1的用戶
- POST請求/api/user/以用戶對象的JSON格式創建新的用戶
- PUT請求/api/user/3以用戶對象作為JSON更新ID為3的用戶
- DELETE請求/api/user/4刪除ID為4的用戶
- DELETE請求/api/user/刪除所有的用戶
package com.jsoft.springmvc.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 com.yiibai.springmvc.model.User; import com.yiibai.springmvc.service.UserService; @RestController public class HelloWorldRestController { @Autowired UserService userService; //Service which will do all data retrieval/manipulation work //-------------------Retrieve All Users-------------------------------------------------------- @RequestMapping(value = "/user/", method = RequestMethod.GET) public ResponseEntity<List<User>> listAllUsers() { List<User> users = userService.findAllUsers(); if(users.isEmpty()){ return new ResponseEntity<List<User>>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND } return new ResponseEntity<List<User>>(users, HttpStatus.OK); } //-------------------Retrieve Single User-------------------------------------------------------- @RequestMapping(value = "/user/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<User> getUser(@PathVariable("id") long id) { System.out.println("Fetching User with id " + id); User user = userService.findById(id); if (user == null) { System.out.println("User with id " + id + " not found"); return new ResponseEntity<User>(HttpStatus.NOT_FOUND); } return new ResponseEntity<User>(user, HttpStatus.OK); } //-------------------Create a User-------------------------------------------------------- @RequestMapping(value = "/user/", method = RequestMethod.POST) public ResponseEntity<Void> createUser(@RequestBody User 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<User> updateUser(@PathVariable("id") long id, @RequestBody User user) { System.out.println("Updating User " + id); User currentUser = userService.findById(id); if (currentUser==null) { System.out.println("User with id " + id + " not found"); return new ResponseEntity<User>(HttpStatus.NOT_FOUND); } currentUser.setName(user.getName()); currentUser.setAge(user.getAge()); currentUser.setSalary(user.getSalary()); userService.updateUser(currentUser); return new ResponseEntity<User>(currentUser, HttpStatus.OK); } //------------------- Delete a User -------------------------------------------------------- @RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE) public ResponseEntity<User> deleteUser(@PathVariable("id") long id) { System.out.println("Fetching & Deleting User with id " + id); User user = userService.findById(id); if (user == null) { System.out.println("Unable to delete. User with id " + id + " not found"); return new ResponseEntity<User>(HttpStatus.NOT_FOUND); } userService.deleteUserById(id); return new ResponseEntity<User>(HttpStatus.NO_CONTENT); } //------------------- Delete All Users -------------------------------------------------------- @RequestMapping(value = "/user/", method = RequestMethod.DELETE) public ResponseEntity<User> deleteAllUsers() { System.out.println("Deleting All Users"); userService.deleteAllUsers(); return new ResponseEntity<User>(HttpStatus.NO_CONTENT); } }
@RestController:首先,我們使用Spring4的新@RestController注釋。 它的注解消除了注釋每個以@ResponseBody的方法。@RestController本身注解為@ResponseBody,並且可以被視為@Controller和@ResponseBody的組合。
@RequestBody:如果一個方法的參數都注解有@RequestBody,Spring將綁定傳入的HTTP請求體(在@RequestMapping提到該法的URL)到這個參數。這樣做Spring將在后台使用HTTP消息轉換為HTTP請求主體轉換成域對象反序列化要求主體域對象的基礎上,接受或Content-Type頭請求。
@ResponseBody:如果一個方法被注解為@ResponseBody,Spring將綁定返回值傳出的HTTP響應體。這樣做Spring將在后台使用HTTP消息轉換器的返回值轉換為HTTP響應體[序列化對象響應正文,根據內容類型出現在請求的HTTP頭。 前面已經提到,在Spring4可能會停止使用此注釋。
ResponseEntity:是一個真正處理。 它代表了整個HTTP響應。一件好事是你可以控制任何進入它東西。可以指定狀態碼,頭和主體。它自帶幾個構造函數執行你想要的 HTTP 響應發送的信息。
@PathVariable這種表示法表示方法參數應綁定到一個URI模板變量“{}”。
基本上,@RestController,@RequestBody,ResponseEntity,@PathVariable都是用Spring 4實現REST API需要知道的。此外,Spring提供了一些支持類來幫助你實現一些定制。
MediaType:通過@RequestMapping注解,你還可以,指定要生產或消費的MediaType(使用生產或消費屬性),通過特定的控制器的方法,以進一步縮小映射。
我們上面使用Postman是一個很好的客戶端測試REST API工具。但是,如果想從應用程序消耗基於REST的Web服務,需要一個REST客戶端的應用程序。其中最流行的HTTP客戶端就是Apache HttpComponents HttpClient。 但在細節上使用這種訪問REST服務太低級。
Spring RestTemplate可以來補救。RestTemplate提供對應於六個主要的HTTP方法,使許多調用RESTful服務只需要一行代碼,就可執行REST最佳實踐的更高層次的方法。
下面顯示的是HTTP方法和相應RestTemplate方法來處理該類型的HTTP請求。
HTTP方法和相應的RestTemplate方法:
- HTTP GET : getForObject, getForEntity
- HTTP PUT : put(String url, Object request, String…urlVariables)
- HTTP DELETE : delete
- HTTP POST : postForLocation(String url, Object request, String… urlVariables), postForObject(String url, Object request, Class responseType, String… uriVariables)
- HTTP HEAD : headForHeaders(String url, String… urlVariables)
- HTTP OPTIONS : optionsForAllow(String url, String… urlVariables)
- HTTP PATCH and others : exchange execute
package com.jsoft.springmvc; import java.net.URI; import java.util.LinkedHashMap; import java.util.List; import org.springframework.web.client.RestTemplate; import com.yiibai.springmvc.model.User; public class SpringRestTestClient { public static final String REST_SERVICE_URI = "http://localhost:8080/Spring4MVCCRUDRestService"; /* GET */ @SuppressWarnings("unchecked") private static void listAllUsers(){ System.out.println("Testing listAllUsers API-----------"); RestTemplate restTemplate = new RestTemplate(); List<LinkedHashMap<String, Object>> usersMap = restTemplate.getForObject(REST_SERVICE_URI+"/user/", List.class); if(usersMap!=null){ for(LinkedHashMap<String, Object> map : usersMap){ System.out.println("User : id="+map.get("id")+", Name="+map.get("name")+", Age="+map.get("age")+", Salary="+map.get("salary"));; } }else{ System.out.println("No user exist----------"); } } /* GET */ private static void getUser(){ System.out.println("Testing getUser API----------"); RestTemplate restTemplate = new RestTemplate(); User user = restTemplate.getForObject(REST_SERVICE_URI+"/user/1", User.class); System.out.println(user); } /* POST */ private static void createUser() { System.out.println("Testing create User API----------"); RestTemplate restTemplate = new RestTemplate(); User user = new User(0,"Sarah",51,134); URI uri = restTemplate.postForLocation(REST_SERVICE_URI+"/user/", user, User.class); System.out.println("Location : "+uri.toASCIIString()); } /* PUT */ private static void updateUser() { System.out.println("Testing update User API----------"); RestTemplate restTemplate = new RestTemplate(); User user = new User(1,"Tomy",33, 70000); restTemplate.put(REST_SERVICE_URI+"/user/1", user); System.out.println(user); } /* DELETE */ private static void deleteUser() { System.out.println("Testing delete User API----------"); RestTemplate restTemplate = new RestTemplate(); restTemplate.delete(REST_SERVICE_URI+"/user/3"); } /* DELETE */ private static void deleteAllUsers() { System.out.println("Testing all delete Users API----------"); RestTemplate restTemplate = new RestTemplate(); restTemplate.delete(REST_SERVICE_URI+"/user/"); } public static void main(String args[]){ listAllUsers(); getUser(); createUser(); listAllUsers(); updateUser(); listAllUsers(); deleteUser(); listAllUsers(); deleteAllUsers(); listAllUsers(); } }
重新啟動服務器(在我們的例子中,在服務器端的數據是固定的)。上面的程序運行。
從上面的客戶端程序輸出
Testing listAllUsers API----------- User : id=1, Name=Sam, Age=30, Salary=70000.0 User : id=2, Name=Tom, Age=40, Salary=50000.0 User : id=3, Name=Jerome, Age=45, Salary=30000.0 User : id=4, Name=Silvia, Age=50, Salary=40000.0 Testing getUser API---------- User [id=1, name=Sam, age=30, salary=70000.0] Testing create User API---------- Location : http://localhost:8080/Spring4MVCCRUDRestService/user/5 Testing listAllUsers API----------- User : id=1, Name=Sam, Age=30, Salary=70000.0 User : id=2, Name=Tom, Age=40, Salary=50000.0 User : id=3, Name=Jerome, Age=45, Salary=30000.0 User : id=4, Name=Silvia, Age=50, Salary=40000.0 User : id=5, Name=Sarah, Age=51, Salary=134.0 Testing update User API---------- User [id=1, name=Tomy, age=33, salary=70000.0] Testing listAllUsers API----------- User : id=1, Name=Tomy, Age=33, Salary=70000.0 User : id=2, Name=Tom, Age=40, Salary=50000.0 User : id=3, Name=Jerome, Age=45, Salary=30000.0 User : id=4, Name=Silvia, Age=50, Salary=40000.0 User : id=5, Name=Sarah, Age=51, Salary=134.0 Testing delete User API---------- Testing listAllUsers API----------- User : id=1, Name=Tomy, Age=33, Salary=70000.0 User : id=2, Name=Tom, Age=40, Salary=50000.0 User : id=4, Name=Silvia, Age=50, Salary=40000.0 User : id=5, Name=Sarah, Age=51, Salary=134.0 Testing all delete Users API---------- Testing listAllUsers API----------- No user exist----------
Maven示例:
https://github.com/easonjim/5_java_example/tree/master/springmvc/Spring4MVCCRUDRestService
參考:
http://www.yiibai.com/spring_mvc/spring-mvc-4-restful-web-services-crud-example-resttemplate.html(以上內容轉自此篇文章)
