1.認識REST 1.1什麼是REST REST是軟體架構的規範體繫結構,它將資源的狀態以適合客戶端的形式從伺服器端發送到客戶端(或相反方向)。在REST中,通過URL進行資源定位,用HTTP動作GET、POST、DELETE、PUSH等)描述操作,完成功能。 道循RESTful風格,可以使開發的接 ...
1.認識REST
1.1什麼是REST
REST是軟體架構的規範體繫結構,它將資源的狀態以適合客戶端的形式從伺服器端發送到客戶端(或相反方向)。在REST中,通過URL進行資源定位,用HTTP動作GET、POST、DELETE、PUSH等)描述操作,完成功能。
道循RESTful風格,可以使開發的介面通用,以便調用者理解介面的作用。基於REST構建的 API 就是 RESTful ( REST 風格)API.
各大機構提供的API基本都是RESTful風格的。這樣可以統一規範,減少溝通、學習和開發 的成本。
1.2 REST的特征
- 客戶一伺服器(client-server):提供服務的伺服器和使用服務的客戶端需要被隔離對待。
- 無狀態(stateless):伺服器端不存儲客戶的請求中的信息,客戶的每一個請求必須包含伺服器處理該請求所需的所有信息,所有的資源都可以通過URI定位,而且這個定位與其他資源無關,也不會因為其他資源的變化而變化。
Restful是典型的基於HTTP的協議。HTTP連接最顯著的特點是:客戶端發送的每次請求都需要伺服器回送響應;在請求結束後,主動釋放連接:
從建立連接到關閉連接的過程稱為“一次連接”,前後的請求沒有必然的聯繫,所以是無狀態的
可緩存(cachable):伺服器必須讓客戶知道請求是否可以被緩存。
- 分層系統(layered System):伺服器和客戶之間的通信必須被標準化。
- 統一介面 (uniform interface):客戶和伺服器之間通信的方法必須統一,REStful風格的 數據元操作 CRUD (create、read、update, delete)分別對應 HTTP 方法—— GET 用來獲取資源,POST用來新建資源,PUT用來更新資源,DELETE用來刪除資源,這樣就統一了數據操作的介面。
- HTTP狀態碼:狀態碼在REST中都有特定的意義:200、201、202、204、400、401、 403、500,比如,401表示用戶身份認證失敗;403表示驗證身份通過了,但資源沒有許可權進行操作。
- 支持按需代碼(Code-On-Demand,可選):伺服器可以提供一些代碼或腳本,併在客戶的運行環境中執行。
1.3 認識HTTP方法與CRUD動作映射
RESTful風格使用同一個URL,通過約定不同的HTTP方法來實施不同的業務。 普通網頁的CRUD和RESTful風格的CRUD的區別,見下表。
可以看出,RESTful風格的CRUD比傳統的CRUD簡單明瞭,它通過HTTP方法來區分增加、修改、刪除和查詢。
1.4 實現RESTfuI風格的數據增加、刪除、修改和查詢
在SpringBoot中,如果要返回JSON數據,則只需要在控制器中用@RestController註解。 如果提交HTTP方法,則只需要使用註解@RequestMapping來實現,它有以下兩個屬性。
- Value:用來制定URI。
- Method:用來制定HTTP請求方法。
為了不重覆編碼,儘量在類上使用@RequestMapping("")來指定上一級URL
使用RESTful風格操作數據的方法見以下代碼。
(1)獲取列表採用的是GET方式,返回List,例如,下麵代碼返回User的List。
@RequestMapping(value = "/",method = RequestMethod.GET)
public List<User> getUserList(){
List<User> userList = new ArrayList<User>(userRepository.findAll());
return userList;
}
(2)增加內容(提交內容)採用的是POST方式,一般返回String類型或int類型的數據,見 以下代碼:
@RequestMapping(value = "/",method = RequestMethod.POST)
public String add(User user){
userRepository.save(user);
return "success";
}
(3)刪除內容,必須採用DEIETE方法。一般都是根據id主鍵進行刪除的
@RequestMapping(value = "/{id}",method = RequestMethod.DELETE)
public String delete(@PathVariable("id")Long id){
userRepository.deleteById(id);
return "success";
}
(4)修改內容,則採用PUT方法。
@RequestMapping(value = "/{id}",method = RequestMethod.PUT)
public String update(User user){
userRepository.save(user);
return "success";
}
(5)查詢內容,和上面獲取列表的方法一樣,也是採用GET方法。
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
public User findUser(@PathVariable("id")Integer id){
Optional<User> user = userRepository.findById(id.longValue());
return user.get();
}
對於RESTful風格的增加、刪除、修改和查詢,可以編寫測試單元,也可以用Postman測試, 分別用GET、POST、PUT、DELETE方法提交測試。雖然這樣實現了 RESTful風格,但還有一 個問題——返回的數據並不統一,在實際生產環境中還需要進行改進,所以需要設計統一的RESTful 風格的數據介面。
2.設計統一的RESTful 風格的數據介面
近年來,隨著移動互聯網的發展,各種類型的客戶端層岀不窮。如果不統一數據介面,則會造成冗餘編碼,增加成本。RESTful風格的API正適合通過一套統一的介面為PC、手機APP等設備提供數據服務。
2.1 版本控制
隨著業務需求的變更、功能的迭代,API的更改是不可避免的。當一個API修改時,就會出現很多問題,比如,可能會在API中新增參數、修改返回的數據類型。這就要考慮根據原先版本API 編寫的客戶端如何保留或順利過渡。所以,需要進行版本控制。
REST不提供版本控制指南,常用的方法可以分為3種。
(1)通過URL
通過URL是最直接的方法,儘管它違背了 URI應該引用唯一資源的原則。當版本更新時,還可以保障客戶端不會受到影響,如下麵使用不同URL來確定不同版本。
二級目錄的方式:
- API 版本 V1: http:// eg.com/api/v1
- API 版本 V2: http://eg.com/api/v2
二級功能變數名稱的方式:
- API 版本 V1: http://v1.eg.com
- API 版本 V2: http://v2.eg.com
還可以包括日期、項目名稱或其他標識符。這些標識符對於開發API的團隊來說足夠有意義, 並旦隨著版本的變化也足夠靈活。
(2)通過自定義請求頭
自定義頭(例如,Accept-version)允許在版本之間保留URL。
(3)通過Accept標頭
客戶端在請求資源之前,必須要指定特定頭,然後API介面負責確定要發送哪個版本的資源
2.2 過濾信息
如果記錄數量很務,則伺服器不可能一次都將它們返回給用戶。API應該提供參數,實現分頁返回結果。下麵是一些常用的參數
- ?limit=10:指定返回記錄的數量。
- ?page=5&size=10:指定第幾頁,以及每頁的記錄數。
- ?search_type=1:指定篩選條件。
2.3 確定HTTP的方法
在RESTful中,HTTP的方法有以下幾種。
- GET:代表請求資源。
- POST:代表添加資源。
- PUT:代表修改資源。PUT是進行全部的修改,大家在編寫修改功能時可能會遇到這樣的情況:只修改了一個欄位,但提交之後導致其他欄位為空。這是因為,其他欄位的值沒有一 起提交,資料庫預設為空值。如果只修改一個或幾個欄位,則可以使用PATCH方法。
- DELETE:代表刪除資源。
- HEAD:代表發送HTTP頭消息,GET中其實也帶了 HTTP頭消息。
- PATCH: PUT與PATCH方法比較相似,但它們的用法卻完全不同,PUT用於替換資源, 而PATCH用於更新部分資源。
- OPTIONS:用於獲取URI所支持的方法。返回的響應消息會在HTTP頭中包含"Allow” 的信息,其值是所支持的方法,如GET。
2.4 確定HTTP的返回狀態
HTTP的返回狀態一般有以下幾種。
- 200:成功。
- 400:錯誤請求。
- 404:沒找到資源。
- 403:禁止。
- 406:不能使用請求內容特性來響應請求資源,比如請求的是HTML文件,但是消費者的 HTTP頭包含了 JSON要求。
- 500:伺服器內部錯誤。
2.5定義統一返回的格式
為了保障前後端的數據交互的順暢,建議規範數據的返回,並採用固定的數據格式封裝。如,
異常信息:
{
"code":"10001",
"msg":"異常信息",
"data":null
}
{
"code":200,
"msg":"成功",
"data":{
"id":1,
"name":"buretuzi",
"age":2
}
}
2.6 為手機APP、PC、H5網頁提供統一風格的API
(1)實現響應的枚舉類
枚舉是一種特殊的數據類型,它是一種"類類型",比類型多了一些特殊的約束。創建枚舉類型要使用“enum”,表示所創建的類型都是java.lang.Enum(抽象類)的子類。見以下代碼:
package com.itheima.exception;
public enum ExceptionMsg {
SUCCESS("200","操作成功"),
FAILED("999999","操作失敗");
private ExceptionMsg(String code,String msg){
this.code = code;
this.msg = msg;
}
private String code;
private String msg;
}
(2)實現返回的對象實體
實現返回的對象實體,返回Code和Message (信息),見以下代碼:
package com.itheima.domain;
public class Response {
private String rspCode="200";
private String rspMsg="操作成功";
}
(3)封裝返回結果
這裡把返回的結果逬行封裝,以顯示數據,見以下代碼:
package com.itheima.dao;
import com.itheima.domain.Response;
public class ReponseData extends Response {
private Object data;
public ReponseData(Object data) {
this.data = data;
}
}
(4)統一處理異常
查看代碼
package com.itheima.handler;
import com.itheima.exception.BusinessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MissingServletRequestParameterException.class)
public Map<String, Object> handleMissingServletRequestParameterException(MissingServletRequestParameterException e){
logger.error("缺少請求參數");
Map<String, Object> map = new HashMap<String, Object>();
map.put("code",400);
map.put("message",e.getMessage());
//如果發生異常,則進行日誌記錄、寫入資料庫或其他處理,此處省略
return map;
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(HttpMessageNotReadableException.class)
public Map<String, Object> handleHttpMessageNotReadableException(HttpMessageNotReadableException e){
logger.error("缺少請求參數");
Map<String, Object> map = new HashMap<String, Object>();
map.put("code",400);
map.put("message",e.getMessage());
//如果發生異常,則進行日誌記錄、寫入資料庫或其他處理,此處省略
return map;
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String, Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e){
logger.error("參數驗證失敗",e);
BindingResult result = e.getBindingResult();
FieldError error = result.getFieldError();
String field = error.getField();
String code = error.getDefaultMessage();
String message = String.format("%s: %s",field,code);
Map<String, Object> map = new HashMap<String, Object>();
map.put("code",code);
map.put("message",message);
//如果發生異常,則進行日誌記錄、寫入資料庫或其他處理,此處省略
return map;
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(BindException.class)
public Map<String, Object> handleBindException(BindException e){
logger.error("缺少請求參數",e);
Map<String, Object> map = new HashMap<String, Object>();
BindingResult result = e.getBindingResult();
FieldError error = result.getFieldError();
String field = error.getField();
String code = error.getDefaultMessage();
String message = String.format("%s: %s",field,code);
map.put("code",code);
map.put("message",message);
//如果發生異常,則進行日誌記錄、寫入資料庫或其他處理,此處省略
return map;
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConstraintViolationException.class)
public Map<String, Object> handleConstraintViolationException(ConstraintViolationException e){
logger.error("缺少請求參數",e);
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
ConstraintViolation<?> violation = violations.iterator().next();
String message = violation.getMessage();
Map<String, Object> map = new HashMap<String, Object>();
map.put("code",400);
map.put("message",message);
//如果發生異常,則進行日誌記錄、寫入資料庫或其他處理,此處省略
return map;
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ValidationException.class)
public Map<String, Object> handleValidationException(ValidationException e){
logger.error("參數驗證失敗",e);
Map<String, Object> map = new HashMap<String, Object>();
map.put("code",400);
map.put("message",e.getMessage());
//如果發生異常,則進行日誌記錄、寫入資料庫或其他處理,此處省略
return map;
}
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Map<String, Object> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e){
logger.error("不支持當前請求方法",e);
Map<String, Object> map = new HashMap<String, Object>();
map.put("code",400);
map.put("message",e.getMessage());
//如果發生異常,則進行日誌記錄、寫入資料庫或其他處理,此處省略
return map;
}
@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
public Map<String, Object> handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e){
logger.error("不支持當前媒體類型",e);
Map<String, Object> map = new HashMap<String, Object>();
map.put("code",415);
map.put("message",e.getMessage());
//如果發生異常,則進行日誌記錄、寫入資料庫或其他處理,此處省略
return map;
}
@ResponseBody
@ExceptionHandler(BusinessException.class)
public Map<String, Object> businessExceptionHandler(BusinessException e){
logger.error("自定義業務失敗",e);
Map<String, Object> map = new HashMap<String, Object>();
map.put("code",e.getCode());
map.put("message",e.getMessage());
//如果發生異常,則進行日誌記錄、寫入資料庫或其他處理,此處省略
return map;
}
@ExceptionHandler(value = Exception.class)
public Map<String, Object> defaultErrorHandler(Exception e){
logger.error("自定義業務失敗",e);
Map<String, Object> map = new HashMap<String, Object>();
map.put("code",500);
map.put("message",e.getMessage());
//如果發生異常,則進行日誌記錄、寫入資料庫或其他處理,此處省略
return map;
}
}
(5)編寫測試控制器
package com.itheima.controller;
import com.itheima.exception.BusinessException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@RequestMapping("BusinessException")
public String testBusinessExceptionStatus(@RequestParam("i") int i){
if (i == 0){
throw new BusinessException(600,"這是自定義異常");
}
return "success";
}
}
運行項目,訪問http://localhost:8080/BusinessException?i=0,在網頁中返回如下 JSON 格式的數據:
(6)實現數據的增加、刪除、修改和查詢控制器
package com.itheima.controller;
import com.itheima.dao.ReponseData;
import com.itheima.dao.UserRepository;
import com.itheima.domain.Response;
import com.itheima.domain.User;
import com.itheima.exception.ExceptionMsg;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@RestController
@RequestMapping("/user")
public class UserController {
protected Response result(ExceptionMsg msg){
return new Response();
}
protected Response result(){
return new Response();
}
@Autowired
private UserRepository userRepository;
@RequestMapping(value = "",method = RequestMethod.GET)
public ReponseData getUserList(){
List<User> list = new ArrayList<>(userRepository.findAll());
return new ReponseData(ExceptionMsg.SUCCESS,list);
}
@RequestMapping(value = "",method = RequestMethod.POST)
public ReponseData add(User user) {
userRepository.save(user);
List<User> list = new ArrayList<>(userRepository.findAll());
return new ReponseData(ExceptionMsg.SUCCESS,user);
}
@RequestMapping(value = "/{id}",method = RequestMethod.DELETE)
public Response delete(@PathVariable("id") Long id) {
userRepository.deleteById(id);
return result(ExceptionMsg.SUCCESS);
}
@RequestMapping(value = "/{id}",method = RequestMethod.PUT)
public ReponseData update(User user) {
userRepository.save(user);
return new ReponseData(ExceptionMsg.SUCCESS,user);
}
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
public ReponseData findUser(@PathVariable("id") int id) {
Optional<User> user = userRepository.findById((long) id);
if (user.get()!=null) {
return new ReponseData(ExceptionMsg.SUCCESS,user);
}
return new ReponseData(ExceptionMsg.FAILED,user);
}
}
3. 用Swagger實現介面文檔
在項目開發中,一般都是前後端分離開發的,需要由前後端工程師共同定義介面:編寫介面文檔,之後大家都根據這個介面文檔進行開發、維護。為了便於編寫和維護穩定,可以使用Swagger來編寫API介面文檔,以提升團隊的溝通效率。
3.1 配置Swagger
(1)添加Swagger依賴
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
(2)創建Swagger配置類
package com.itheima.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Swagger配置類
* 在與Spring Boot集成時,放在與Application.java同級的目錄下
* 通過註解@Configuration讓Spring來載入該類配置
* 再通過註解@EnableSwagger2來啟用Swagger2
*/
@Configuration
@EnableSwagger2
public class Swagger2 {
/**
* 創建API應用
* apiInfo增加API相關信息
* 通過select()函數返回一個ApiSelectorBuilder實例,用來控制哪些介面暴露給Swagger來展現
* 本例採用指定掃描的包路徑來定義指定要建立API的目錄
*/
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage(""))
.paths(PathSelectors.any())
.build();
}
/**
* 創建該API的基本信息(這些基本信息會展現在文檔頁面中)
* 訪問地址:http:/項目實際地址/swagger-ui.html
*/
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("RESTful APIs")
.description("RESTful APIs")
.termsOfServiceUrl("http://localhost:8080/")
.contact("long")
.version("1.0")
.build();
}
}
代碼解釋如下。
- @Configuration: 讓Spring來載入該類配置。
- @EnableSwagger2: 啟用 Swagger2.createRestApi 函數創建 Docket 的 Bean
- apilnfo():用來展示該API的基本信息。
- select():返回一個ApiSelectorBuilder實例,用來控制哪些介面暴露給Swagger來展現。
- apis(RequestHandlerSelectors.basePackage()):配置包掃描路徑。Swagger 會掃描包下所有Controller定義的API,並產生文檔內容。如果不想產生API,則使用註解 @Apilgnore
(3)編寫介面文檔
在完成上述配置後,即生成了文檔,但是這樣生成的文檔主要針對請求本身,而描述自動根據方法等命名產生,對用戶並不友好。所以,通常需要自己增加一些說明以豐富文檔內容。可以通過以下註解來增加說明。
- @Api:描述類/介面的主要用途。
- @ApiOperation:描述方法用途,給API增加說明。
- @ApilmplicitParam: 擋述方法的參數,給參數増加說距。
- @ApilmplicitParams: 描述方法的參數(Multi-Params ),給參數增加說明。
- @Apilgnore:忽略某類/方法/參數的義檔。
@ApiOperation(value = "刪除用戶",notes = "根據URL的id來指定刪除對象")
@ApiImplicitParam(name = "id",value = "文章ID",required = true,dataType = "Long")
public String del(@PathVariable("id") Long id) {
userRepository.deleteById(id);
return "SUCCESS";
}
完成上述代碼後,啟動項目,訪問http://localhost:8080/swagger-ui.html就能看到所展示的RESTful API的頁面,可以通過單擊具體的API測試請求,來查看代碼中配置的信息,以及參數的描述信息。
3.2 用RestTemplate發起請求
(1)認識 RestTemplate
在Java應用程式中訪問RESTful服務,可以使用Apache的HttpClient來實現。不過此方法使用起來太煩瑣。Spring提供了一種簡單便捷的模板類一RestTemplate來進行操作。RestTemplate是Spring提供的用於訪問REST服務的客戶端,它提供了多種便捷訪問遠程HTTP 服務的方法,能夠大大提高客戶端的編寫效率。
RestTemplate用於同步Client端的核心類,簡化與HTTP服務的通信。在預設情況下, RestTemplate預設依賴JDK的HTTP連接工具。也可以通過setRequestFactory屬性切換到不同的 HTTP 源,比如 Apache HttpComponents, Netty 和 OkHttp。
RestTemplate簡化了提交表單數據的難度,並附帯自動轉換為JSON格式數據的功能。該類的入口主要是根據HTTP的6種方法制定的,見下表。
此外,exchange和excute也可以使用上述方法。
RestTemplate 預設使用 HttpMessageConverter 將 HTTP 消息轉換成 POJO,或從 POJO 轉換成HTTP消息,預設情況下會註冊MIME類型的轉換器,但也可以通過setMessageConverters 註冊其他類型的轉換器。
(2)用 RestTemplate 發送 GET 請求
1.創建測試實體
package com.intehel.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private long id;
private String name;
}
2.創建用於測試的API
package com.intehel.controller;
import com.intehel.domain.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@RequestMapping(value = "/getParameter", method = RequestMethod.GET)
public User getParameter(User user){
return user;
}
@RequestMapping(value = "/getuser1", method = RequestMethod.GET)
public User user1(){
return new User(1,"buretuzi");
}
@RequestMapping(value = "/postuser", method = RequestMethod.POST)
public User postUser(User user){
System.out.println("name:"+user.getName());
System.out.println("id:"+user.getId());
return user;
}
}
3.使用getForEntity測試
(1)返回String,不帶參數,見以下代碼:
package com.intehel;
import org.junit.Test;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
public class test {
@Test
public void nparameters(){
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
RestTemplate client = restTemplateBuilder.build();
ResponseEntity<String> responseEntity = client.getForEntity("http://localhost:8080/getuser1",String.class);
System.out.println(responseEntity.getBody());
}
}
控制台列印結果:
11:01:35.973 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8080/getuser1
11:01:35.991 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*]
11:01:36.073 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
11:01:36.073 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"
{"id":1,"name":"buretuzi"}
(2)返回String,帶參數的例子。
在調用服務提供者提供的介面時,有時需要傳遞參數,有以下兩種不同的方式。
①用一個數字做占位符。最後是一個可變長度的參數,用來替換前面的占位符。使用方法見以下代碼:
@Test
public void withparameters(){
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
RestTemplate client = restTemplateBuilder.build();
ResponseEntity<String> responseEntity = client.getForEntity("http://localhost:8080/getParameter?name={1}&id={2}",String.class,"buretuzi",2);
System.out.println(responseEntity.getBody());
}
列印結果:
11:06:20.893 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8080/getParameter?name=buretuzi&id=2
11:06:20.893 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*]
11:06:20.908 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
11:06:20.908 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"
{"id":2,"name":"buretuzi"}
② 使用name={name}這種形式。最後一個參數是一個map, map的key即為前邊占位符的名字,map的value為參數值。使用方法見以下代碼:
@Test
public void withparameters2(){
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
RestTemplate client = restTemplateBuilder.build();
Map<String,String> map = new HashMap<String,String>();
map.put("name", "buretuzi");
ResponseEntity<String> responseEntity = client.getForEntity("http://localhost:8080/getParameter?name={name}&id=3",String.class,map);
System.out.println(responseEntity.getBody());
}
列印結果:
11:19:28.842 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8080/getParameter?name=buretuzi&id=3
11:19:28.848 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*]
11:19:28.880 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
11:19:28.880 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"
{"id":3,"name":"buretuzi"}
(3)返回對象,見以下代碼:
@Test
public void restUser(){
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
RestTemplate client = restTemplateBuilder.build();
ResponseEntity<User> responseEntity = client.getForEntity("http://localhost:8080/getuser1",User.class);
System.out.println(responseEntity.getBody().getId());
System.out.println(responseEntity.getBody().getName());
}
列印結果:
1
buretuzi
4.使用 getForObject
getForObject函數是對getForEntity函數的進一步封裝。如果你只關註返回的消息體的內容, 對其他信息都不關註,則可以使用getForObject,見以下代碼:
@Test
public void getForObject(){
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
RestTemplate client = restTemplateBuilder.build();
User user = client.getForObject("http://localhost:8080/getuser1",User.class);
System.out.println(user.getName());
}
(3)用 RestTemplate 發送 POST 請求
在 RestTemplate 中,POST 請求可以通過 postForEntity、postForObject、postForLocation、exchange四種方法來發起。
1.方法一:使用 postForEntity
- postForEntity(String url,Object request,Class responseType,Object... urlVariables)
- postForEntity(String url,Object request,Class responseType,Map urlVariables)
- postForEntity(String url,Object request,Class responseType)
2.方法二:使用 postForObject
- postForObject(String url,Object request,Class responseType,Object... urlVariables)
- postForObject(String url,Object request,Class responseType,Map urlVariables)
- postForObject(String url,Object request,Class responseType)
3.方法三:使用 postForLocation
postForLocation也用於提交資源。在提交成功之後,會返回新資源的URI。它的參數和前面兩種方法的參數基本一致,只不過該方法的返回值為URI,表示新資源的位置
- postForLocation(String url,Object request,Object... urlVariables)
- postForLocation(String url,Object request,Map urlVariables)
- postForLocation(String url,Object request)
4.方法四:使用exchange
使用exchange方法可以指定調用方式,使用方法如下:
ResponseEntity<String> response=tempIate.exchang(newUrl, HttpMethod.DELETE, request, String.class);
5.實現發送POST請求
(1)使用 postForEntity
@Test
public void postForEntity(){
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
RestTemplate client = restTemplateBuilder.build();
MultiValueMap<String,Object> paramMap = new LinkedMultiValueMap<String,Object>();
paramMap.add("name","buretuzi");
paramMap.add("id",4);
ResponseEntity<User> responseEntity = client.postForEntity("http://localhost:8080/postuser",paramMap,User.class);
System.out.println(responseEntity.getBody().getName());
}
代碼解釋如下。
- MultiValueMap:封裝參數,千萬不要替換為Map與HashMap,否則參數無法被傳遞
- postForEntity("url", paramMap, User.class):參數分別表示要調用的服務的地址、上傳的參數、返回的消息體的數據類型。
運行測試單元,控制台輸出如下結果:
11:39:07.001 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP POST http://localhost:8080/postuser
11:39:07.032 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[application/json, application/*+json]
11:39:07.032 [main] DEBUG org.springframework.web.client.RestTemplate - Writing [{name=[buretuzi], id=[4]}] with org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter
11:39:07.482 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
11:39:07.482 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [com.intehel.domain.User]
buretuzi
(2)使用 postForObject
postForObject和getForObject相對應,只關註返回的消息體,見以下代碼:
@Test
public void postForObject(){
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
RestTemplate client = restTemplateBuilder.build();
MultiValueMap<String,Object> paramMap = new LinkedMultiValueMap<String,Object>();
paramMap.add("name","buretuzi");
paramMap.add("id",4);
String response = client.postForObject("http://localhost:8080/postuser",paramMap,String.class);
System.out.println(response);
}
運行測試單元,控制台輸岀如下結果:
11:44:46.470 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP POST http://localhost:8080/postuser
11:44:46.470 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*]
11:44:46.486 [main] DEBUG org.springframework.web.client.RestTemplate - Writing [{name=[buretuzi], id=[4]}] with org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter
11:44:46.918 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
11:44:46.918 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"
{"id":4,"name":"buretuzi"}
(3)使用postForexchange,見以下代碼:
@Test
public void postForExchange(){
MultiValueMap<String,Object> paramMap = new LinkedMultiValueMap<String,Object>();
paramMap.add("name","buretuzi");
paramMap.add("id",4);
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
RestTemplate client = restTemplateBuilder.build();
HttpHeaders headers = new HttpHeaders();
HttpEntity<MultiValueMap<String,Object>> httpEntity = new HttpEntity<MultiValueMap<String,Object>>(paramMap,headers);
ResponseEntity<String> response = client.exchange("http://localhost:8080/postuser", HttpMethod.POST, httpEntity,String.class,paramMap);
System.out.println(response.getBody());
}
運行測試單元,控制台輸岀如下結果:
11:59:12.988 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP POST http://localhost:8080/postuser
11:59:13.004 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*]
11:59:13.004 [main] DEBUG org.springframework.web.client.RestTemplate - Writing [{name=[buretuzi], id=[4]}] with org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter
11:59:13.436 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
11:59:13.436 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"
{"id":4,"name":"buretuzi"}
(4)使用 postForLocation
它用於提交數據,並獲取返回的URI。一般登錄、註冊都是POST請求,操作完成之後,跳轉到某個頁面,這種場景就可以使用postForLocation所以,先要添加處理登錄的API,見以下代碼:
package com.intehel.controller;
import com.intehel.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@Controller
public class TestController {
@RequestMapping(path = "success",method = RequestMethod.POST)
@ResponseBody
public String loginSuccess(String name){
return "welcome"+name;
}
@RequestMapping(path = "post",method = RequestMethod.POST)
public String post(HttpServletRequest request,
@RequestParam(value = "name",required = false)String name,
@RequestParam(value = "password",required = false)String password,
@RequestParam(value = "id",required = false)Integer id){
return "redirect:/success?name="+name+"&id="+id;
}
}
然後使用postForLocation請求,用法見以下代碼:
@Test
public void postForLocation(){
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
MultiValueMap<String,Object> paramMap = new LinkedMultiValueMap<String,Object>();
paramMap.add("name","buretuzi");
paramMap.add("id",4);
RestTemplate client = restTemplateBuilder.build();
URI response = client.postForLocation("http://localhost:8080/post",paramMap);
System.out.println(response);
}
運行測試單元,控制台輸出如下結果:
13:59:06.415 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP POST http://localhost:8080/post
13:59:06.415 [main] DEBUG org.springframework.web.client.RestTemplate - Writing [{name=[buretuzi], id=[4]}] with org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter
13:59:06.951 [main] DEBUG org.springframework.web.client.RestTemplate - Response 302 FOUND
http://localhost:8080/success?name=buretuzi&id=4
(4)用 RestTemplate 發送 PUT和DELETE 請求
1.PUT請求
在RestTemplate中,發送“修改”請求和前面介紹的postForEntity方法的參數基本一致, 只是修改請求沒有返回值,用法如下:
@Test
public void put(){
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
RestTemplate client = restTemplateBuilder.build();
User user = new User();
user.setName("buretuzi");
client.put("http://localhost:8080/{1}",user,4);
}
2.DELETE 請求
刪除請求,可以通過調用DELETE方法來實現,用法見以下代碼:
@Test
public void delete(){
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
RestTemplate client = restTemplateBuilder.build();
client.delete("http://localhost:8080/{1}",4);
}
最後的“4”用來替換前面的占位符{1}。