SpringBoot(17)介面架構風格—RESTful與Swagger

来源:https://www.cnblogs.com/liwenruo/archive/2022/07/26/16513573.html
-Advertisement-
Play Games

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、202204、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}。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Lambda 表達式(lambda expression)是一個匿名函數,Lambda表達式基於數學中的λ演算得名,直接對應於其中的lambda抽象(lambda abstraction),是一個匿名函數,即沒有函數名的函數。Lambda表達式可以表示閉包(註意和數學傳統意義上的不同)。 閉包就是能... ...
  • 總結 這場表演邀請了三位角色:run.sh、main.py、path.sh,拍攝場地選在了 Windows -> Git Bash 群演1號 run.sh #!/usr/bin bash . ./path.sh || exit -1 # demo.py無法直接找到是因為 $PATH中已經沒有 工作目 ...
  • Java方法01 1.什麼是方法? Java是語句的集合,它們在一起執行一個功能 方法是解決一類問題的步驟的有序集合 方法包含於類或者對象中 方法在程式中被創建,在其他地方被引用 設計方法的原則:方法的本意是功能塊,就是實現某個功能的語句塊的集合。我們設計方法的時候,最好保持方法的==原子性,就是一 ...
  • 前言 嗨嘍~大家好呀,這裡是魔王吶 環境使用: Python 3.8 Pycharm 模塊使用: requests > 數據請求模塊 需要安裝 pip install requests re 正則表達式 內置模塊 不需要安裝 os 文件操作模塊 內置模塊 不需要安裝 --> 自動創建文件夾 把每個英 ...
  • 前言: 引出類與對象(oop) -- 為什麼需要類與對象(oop) 例:我有兩隻小狗,有姓名,年齡,性別,怎麼完成該操作? //用變數 String DogName = "小白"; int DogAge = 6; char DogSex= '公' String DogName = "小新"; int ...
  • SpringMVC是什麼? SpringMVC 也叫Spring web mvc。是Spring 框架的一部分,是在Spring3.0 後發佈的。 這裡對SpringMVC框架進行一個簡單的介紹: springmvc是spring框架的一個模塊,springmvc和spring無需通過中間整合層進行 ...
  • 兄弟們,今天咱們試試用Python從文件中讀取學生成績,並計算最高分/最低分/平均分。 涉及知識點 文件讀寫 基礎語法 字元串處理 迴圈遍歷 代碼展示 模塊 import platform # 我還給大家準備了這些資料:Python視頻教程、100本Python電子書、基礎、爬蟲、數據分析、web開 ...
  • 多商戶商城系統,也稱為B2B2C(BBC)平臺電商模式多商家商城系統。可以快速幫助企業搭建類似拼多多/京東/天貓/淘寶的綜合商城。 多商戶商城系統支持商家入駐加盟,同時滿足平臺自營、旗艦店等多種經營方式。平臺可以通過收取商家入駐費,訂單交易服務費,提現手續費,簡訊通道費等多手段方式,實現整體盈利。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...