<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      Fork me on GitHub

      SpringBoot優雅開發REST API最佳實踐

      寫在前面

      博主最近在做一個數據服務的項目,而這個數據服務的核心就是對外暴露的API,值得高興的這是一個從0開始的項目,所以終于不用受制于“某些歷史”因素去續寫各種風格的Controller,可以在項目伊始就以規范的技術和統一形式去搭建API。借此機會,梳理和匯總一下基于SpringBoot項目開發REST API的技術點和規范點。

      接口服務主要由兩部分組成,即參數(輸入)部分,響應(輸出)部分。其中在SpringBoot中主要是Controller層作為API的開發處,其實在架構層面來講,Controller本身是一個最高的應用層,它的職責是調用、組裝下層的interface服務數據,核心是組裝和調用,不應該摻雜其他相關的邏輯。

      但是往往很多項目里針對Controller部分的代碼都是十分混亂,有的Controller兼顧各種if else的參數校驗,有的甚至直接在Controller進行業務代碼編寫;對于Controller的輸出,有的粗略的加個外包裝,有的甚至直接把service層的結構直接丟出去;對于異常的處理也是各種各樣。

      以上對于Controller相關的問題,這里統一用一系列Controller的封裝處理來提供優化思路。優雅且規范的開發REST API需要做以下幾步:

      • 接口版本控制
      • 參數校驗
      • 異常捕獲處理
      • 統一響應封裝
      • 接口文檔的維護和更新

      @RestController注解

      直接來看@RestController源碼

      @Target({ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Controller
      @ResponseBody
      public @interface RestController {
          @AliasFor(
              annotation = Controller.class
          )
          String value() default "";
      }

      @RestController注解等價于@Controller和@@ResponseBody,@ResponseBody注解的作用是告訴Spring MVC框架,該方法的返回值應該直接寫入HTTP響應體中,而不是返回一個視圖(View)。當一個控制器方法被標記為 @ResponseBody 時,Spring MVC會將方法的返回值序列化成JSON或XML等格式,然后發送給客戶端。更適用于REST API的構建。

      所以針對Controller接口的開發,直接使用@RestController為好。它會自動將Controller下的方法返回內容轉為REST API的形式

      例如:

      @RestController
      @RequestMapping("/dataserver/manage")
      public class DataServerController{
          
          @PostMapping("/search")
          public Response searchData(@RequestBody SearchTaskDto param){
              return Response.success(taskScheduleManagerService.searchTaskForPage(param));
          }
      }

      接口版本管理

      對于API來講,一般是對外服務的基礎,不能隨意變更,但是隨著需求和業務不斷變化,接口和參數也會發生相應的變化。此時盡可能保證“開閉原則”,以新增接口或增強接口功能來支撐,此時就需要對API的版本進行維護,以版本號來確定同一接口的不同能力,一般版本都基于url來控制

      例如:

      • http://localhost:8080/dataserver/v1/queryAccount
      • http://localhost:8080/dataserver/v2/queryAccount:相比v1版本增強了參數查詢的靈活性

      進行API版本控制主要分三步:

      • 定義版本號注解
      • 編寫版本號匹配邏輯處理器
      • 注冊處理器

      定義版本號注解

      /**
       * API版本控制注解
       */
      @Target({ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      public @interface ApiVersion {
          /**
           *版本號,默認為1
           */
          int value() default 1;
      }

      該注解可直接使用在Controller類上

      @RestController
      @RequestMapping("dataserver/{version}/account")
      @ApiVersion(2)//輸入版本號,對應{version}
      public class AccountController{
          @GetMapping("/test")
          public String test() {
              return "XXXX";
          }
      }

      編寫版本號匹配邏輯處理器

      首先定義一個條件匹配類,對應解析Url中的version與ApiVersion注解

      /**
      *實現Request的條件匹配接口
      *
      **/
      public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
          private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile(".*v(\\d+).*");
       
          private int apiVersion;
       
          ApiVersionCondition(int apiVersion) {
              this.apiVersion = apiVersion;
          }
       
          private int getApiVersion() {
              return apiVersion;
          }
       
       
          @Override
          public ApiVersionCondition combine(ApiVersionCondition apiVersionCondition) {
              return new ApiVersionCondition(apiVersionCondition.getApiVersion());
          }
       
          @Override
          public ApiVersionCondition getMatchingCondition(HttpServletRequest httpServletRequest) {
              Matcher m = VERSION_PREFIX_PATTERN.matcher(httpServletRequest.getRequestURI());
              if (m.find()) {
                  Integer version = Integer.valueOf(m.group(1));
                  if (version >= this.apiVersion) {
                      return this;
                  }
              }
              return null;
          }
       
          @Override
          public int compareTo(ApiVersionCondition apiVersionCondition, HttpServletRequest httpServletRequest) {
              return apiVersionCondition.getApiVersion() - this.apiVersion;
          }
      }

      這里補充一下 RequestCondition<ApiVersionCondition>相關概念:

      它是 Spring 框架中用于請求映射處理的一部分。在 Spring MVC 中,RequestCondition 接口允許開發者定義自定義的請求匹配邏輯,這可以基于請求的任何屬性,例如路徑、參數、HTTP 方法、頭部等。相關的應用場景包括:

      1. 路徑匹配(Path Matching):使用 PatternsRequestCondition 來定義請求的路徑模式,支持 Ant 風格的路徑模式匹配,如 /api/* 可以匹配所有 /api 開頭的請求路徑 。

      2. 請求方法匹配(Request Method Matching):通過 RequestMethodsRequestCondition 來限制請求的 HTTP 方法,例如只允許 GET 或 POST 請求 。

      3. 請求參數匹配(Request Params Matching):使用 ParamsRequestCondition 來定義請求必須包含的參數,例如某些接口可能需要特定的查詢參數才能訪問 。

      4. 請求頭匹配(Request Headers Matching)HeadersRequestCondition 允許定義請求頭的條件,例如某些接口可能需要特定的認證頭部才能訪問 。

      5. 消費媒體類型匹配(Consumes Media Type Matching)ConsumesRequestCondition 用來定義控制器方法能夠處理的請求體媒體類型,通常用于 RESTful API 中,例如只處理 application/json 類型的請求體 。

      6. 產生媒體類型匹配(Produces Media Type Matching)ProducesRequestCondition 定義了控制器方法能夠返回的媒體類型,這通常與 Accept 請求頭結合使用以確定響應的格式 。

      7. 自定義條件匹配:開發者可以通過實現 RequestCondition 接口來定義自己的匹配邏輯,例如根據請求中的版本號來路由到不同版本的 API,實現 API 的版本控制 。

      8. 組合條件匹配(Composite Conditions Matching):在某些情況下,可能需要根據多個條件來匹配請求,CompositeRequestCondition 可以將多個 RequestCondition 組合成一個條件來進行匹配 。

      9. 請求映射的優先級選擇(Priority Selection for Request Mapping):當存在多個匹配的處理器方法時,RequestConditioncompareTo 方法用于確定哪個條件具有更高的優先級,以選擇最合適的處理器方法 。

      創建一個版本映射處理器,使用 ApiVersionCondition 作為自定義條件來處理請求映射。當 Spring MVC 處理請求時,它會使用這個自定義的映射處理器來確定哪個版本的 API 應該處理請求。

      public class ApiRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
          private static final String VERSION_FLAG = "{version}";
         
          /**
           *檢查類上是否有 @RequestMapping 注解,如果有,它會構建請求映射的 URL。如果 URL 中包含版本 
           *標識 VERSION_FLAG,并且類上有 ApiVersion 注解,它將創建并返回一個 ApiVersionCondition 
           *實例,表示這個類關聯的 API 版本。
           **/
          private static RequestCondition<ApiVersionCondition> createCondition(Class<?> clazz) {
              RequestMapping classRequestMapping = clazz.getAnnotation(RequestMapping.class);
              if (classRequestMapping == null) {
                  return null;
              }
              StringBuilder mappingUrlBuilder = new StringBuilder();
              if (classRequestMapping.value().length > 0) {
                  mappingUrlBuilder.append(classRequestMapping.value()[0]);
              }
              String mappingUrl = mappingUrlBuilder.toString();
              if (!mappingUrl.contains(VERSION_FLAG)) {
                  return null;
              }
              ApiVersion apiVersion = clazz.getAnnotation(ApiVersion.class);
              return apiVersion == null ? new ApiVersionCondition(1) : new ApiVersionCondition(apiVersion.value());
          }
       
          @Override
          protected RequestCondition<?> getCustomMethodCondition(Method method) {
              return createCondition(method.getClass());
          }
       
          @Override
          protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
              return createCondition(handlerType);
          }
      }

      注冊處理器

      將上述的處理器注冊到SpringMvc的處理流程中

      @Configuration
      public class WebMvcRegistrationsConfig implements WebMvcRegistrations {
          @Override
          public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
              return new ApiRequestMappingHandlerMapping();
          }
      }

      驗證:

      @RestController
      @RequestMapping("dataserver/{version}/account")
      @ApiVersion(1)
      public class AccountOneController {
       
          @GetMapping("/test")
          public String test() {
              return "測試接口,版本1";
          }
          @GetMapping("/extend")
          public String extendTest() {
              return "版本1的測試接口延申";
          }
      }
      
      
      @RestController
      @RequestMapping("dataserver/{version}/account")
      @ApiVersion(2)
      public class AccountTwoController {
          @GetMapping("/test")
          public String test() {
              return "測試接口,版本2";
          }
      }

      針對test接口進行不同版本的請求:

      針對Account擴展版本調用上一版本接口

      當請求對應的版本不存在接口時,會匹配之前版本的接口,即請求/v2/account/extend 接口時,由于v2 控制器未實現該接口,所以自動匹配v1 版本中的接口。這就實現了API版本繼承

      參數校驗

      @Validated注解

      @Validated 是一個用于 Java 應用程序中的注解,特別是在 Spring 框架中,以指示目標對象或方法需要進行驗證。這個注解通常與 JSR 303/JSR 380 規范的 Bean Validation API 結合使用,以確保數據的合法性和完整性。

      @Validated注解的三種用法:

      方法級別驗證:當 @Validated 注解用在方法上時,它指示 Spring 在調用該方法之前執行參數的驗證。如果參數不符合指定的驗證條件,將拋出 MethodArgumentNotValidException。

      @PostMapping("/user")
      @Validated
      public ResVo createUser(@RequestBody @Valid User user) {
          // 方法實現
      }

      類級別驗證:將 @Validated 注解用在類上,表示該類的所有處理請求的方法都會進行驗證。這可以減少在每個方法上重復注解的需要。

      @RestController
      @Validated
      public class UserController {
          // 類中的所有方法都會進行驗證
      }

      組合注解:Spring 還提供了 @Valid 注解,它是 @Validated 的一個更簡單的形式,只觸發驗證并不指定特定的驗證組(Validation Groups)。@Validated 允許你指定一個或多個驗證組,這在需要根據不同情況執行不同驗證規則時非常有用。

      @Validated(OnCreate.class)
      public void createUser(User user) {
          // 只使用 OnCreate 組的驗證規則
      }

      使用注解進行參數校驗

      在REST API中進行參數驗證一般使用方法級別驗證即可,即對參數Dto的類內信息進行驗證,例如一個分頁的查詢參數類:

      @Data
      public class BaseParam implements Serializable {
      
          @NotNull(message = "必須包含關鍵字")
          private String  keyFilter;
      
          @Min(value = 1,message = "頁碼不可小于1")
          private int pageNo;
      
          @Max(value = 100,message = "考慮性能問題,每頁條數不可超過100")
          private int pageSize;
      
      }

      在Controller中配合@Validated使用:

        @PostMapping("/findProductByVo")
          public PageData findByVo(@Validated ProductParam param) {
              //……業務邏輯
              return PageData.success(data);
          }

      此時如果前端傳入參數不合法,例如pageNo為0又或者productType不存在,則會拋出MethodArgumentNotValidException 的異常。稍后對于異常進行處理即可完成參數的驗證。

      這里的@Max@Min@NotNull 注解屬于 Bean Validation API 的一部分,這是一個 JSR 303/JSR 380 規范,用于在 Java 應用程序中提供聲明式驗證功能。這些注解用于約束字段值的范圍和非空性。類似的注解還有:

      注解 作用

      @NotNull

      驗證注解的字段值不能為 null。
      @NotEmpty 與 @NotNull 類似,但用于集合或字符串,驗證注解的字段值不能為 null,且對于字符串,長度不能為 0。
      @NotBlank 驗證注解的字段值不能為 null,且不能是空白字符串(空白包括空格、制表符等)。
      @Min(value) 驗證注解的字段值是否大于或等于指定的最小值。value 參數接受一個整數。
      @Max(value) 驗證注解的字段值是否小于或等于指定的最大值。value 參數接受一個整數。
      @Size(min, max) 驗證字符串或集合的大小在指定的最小值和最大值之間。
      @Pattern(regex) 驗證字段值是否符合指定的正則表達式。

      注:SpringBoot 2.3.1 版本默認移除了校驗功能,如果想要開啟的話需要添加以上依賴

              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-validation</artifactId>
              </dependency>

      統一異常捕獲

      @RestControllerAdvice注解

      @RestControllerAdvice 是 @ResponseBody+@ControllerAdvice的集合注解,用于定義一個控制器級別的異常處理類。一般用來進行全局異常處理,在 @RestControllerAdvice 類中處理異常后,可以直接返回一個對象,該對象會被轉換為 JSON 或 XML 響應體,返回給客戶端。

      使用@RestControllerAdvice注解處理參數異常

      使用@Validated和 Bean Validation API 的注解進行參數校驗后,當出現不符合規定的參數會拋出MethodArgumentNotValidException 異常,這里就可以使用@RestControllerAdvice注解來創建一個全局Controller異常攔截類,來統一處理各類異常

      @RestControllerAdvice
      public class ControllerExceptionAdvice {
      
          @ExceptionHandler({MethodArgumentNotValidException .class})//此處可以根據參數異常的各類情況進行相關異常類的綁定
          public String MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
              // 從異常對象中拿到ObjectError對象
              ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
              return "參數異常錯誤";
          }
      }

      這里只以 MethodArgumentNotValidException 異常進行攔截,在@RestControllerAdvice類內可以創建多個方法,通過@ExceptionHandler對不同的異常進行定制化處理,這樣當Controller內發生異常,都可以在@RestControllerAdvice類內進行截獲、處理、返回給客戶端安全的信息。

      @RestControllerAdvice
      public class ControllerExceptionAdvice {
      
          //HttpMessageNotReadableException異常為webJSON解析出錯
          @ExceptionHandler({HttpMessageNotReadableException.class})
          public String MethodArgumentNotValidExceptionHandler(HttpMessageNotReadableException e) 
         {
              return "參數錯誤";
          }
      
           @ExceptionHandler({XXXXXException .class})
          public String otherExceptionHandler(Exception e) {
             
              ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
              return objectError..getDefaultMessage();
          }
      }

      統一響應封裝

      首先,進行統一的響應格式,這里需要封裝一個固定返回格式的結構對象:ResponseData

      public class Response<T> implements Serializable {
          private Integer code;
          private String msg;
          private T data;
      
          public Response() {
              this.code = 200;
              this.msg = "ok";
              this.data = null;
          }
      
          public Response(Integer code, String msg, T data) {
              this.code = code;
              this.msg = msg;
              this.data = data;
          }
      
          public Response(String msg, T data) {
              this(200, msg, data);
          }
      
          public Response(T data) {
              this("ok", data);
          }
      
          public static <T> Response<T> success(T data) {
              return new Response(data);
          }
      
          public Integer getCode() {
              return this.code;
          }
      
          public void setCode(Integer code) {
              this.code = code;
          }
      
          public String getMsg() {
              return this.msg;
          }
      
          public void setMsg(String msg) {
              this.msg = msg;
          }
      
          public T getData() {
              return this.data;
          }
      
          public void setData(T data) {
              this.data = data;
          }
      
          public String toJsonString() {
              String out = "";
              try {
                  out = JSONUtil.toJsonPrettyStr(this);
              } catch (Exception var3) {
                  this.setData(null);
                  var3.printStackTrace();
              }
              return out;
          }
      }

      統一狀態碼

      其中對于相關的狀態碼最好進行統一的封裝,便于以后的開發,創建狀態枚舉:

      //面向接口開發,首先定義接口
      public interface StatusCode {
          Integer getCode();
          String getMessage();
      }
      //創建枚舉類
      public enum ResponseStatus implements StatusCode{
      
          //正常響應
          SUCCESS(200, "success"),
          //服務器內部錯誤
          FAILED(500, " Server Error"),
          //參數校驗錯誤
          VALIDATE_ERROR(400, "Bad Request");
      
          //……補充其他內部約定狀態
      
          private int code;
          private String msg;
      
          ResponseStatus(int code, String msg) {
              this.code = code;
              this.msg = msg;
          }
      
          @Override
          public Integer getCode() {
              return this.code;
          }
      
          @Override
          public String getMessage() {
              return this.msg;
          }
      }

      統一返回結構 

      將上述的ResponseData中狀態類相關替換為枚舉

      public class Response<T> implements Serializable {
          private Integer code;
          private String msg;
          private T data;
      
          public Response() {
              this.code = 200;
              this.msg = "success";
              this.data = null;
          }
          public Response(StatusCode status, T data) {
              this.code = status.getCode();
              this.msg = status.getMssage();
              this.data = data;
          }
          public Response(T data) {
              this(ResponseStatus.SUCCESS, data);
          }
          public static <T> Response<T> success(T data) {
              return new Response(data);
          }
          public Integer getCode() {
              return this.code;
          }
          public void setCode(Integer code) {
              this.code = code;
          }
          public String getMsg() {
              return this.msg;
          }
          public void setMsg(String msg) {
              this.msg = msg;
          }
          public T getData() {
              return this.data;
          }
          public void setData(T data) {
              this.data = data;
          }
          public String toJsonString() {
              String out = "";
              try {
                  out = JSONUtil.toJsonPrettyStr(this);
              } catch (Exception var3) {
                  this.setData(null);
                  var3.printStackTrace();
              }
              return out;
          }
      }

      這樣Controller的接口統一返回格式就是標準的結構了。

      {
        "code":200,
        "msg":"success",
        "data":{
          "total":123,
          "record":[]
        }
      }

      統一封裝Controller響應

      有了統一響應體的Controller在返回時可以這樣寫:

          @PostMapping("/search")
          @Operation(summary = "分頁查詢任務")
          public Response searchData(@RequestBody SearchParam param){
              return Response.success(XXXXService.searchForPage(param));
          }

      即便如此,團隊開發中可能還會出現換個人新寫Controller不知道有統一返回體這回事,為了更保險,可以通過AOP進行統一對結果進行封裝,不論Controller返回啥,到客戶端的數據都包含一個包裝體。

      具體實現是使用@RestControllerAdvice類實現ResponseBodyAdvice接口來完成。

      @RestControllerAdvice
      public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {
          @Override
          public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
              // 返回結構是Response類型都不進行包裝
              return !methodParameter.getParameterType().isAssignableFrom(Response.class);
          }
      
         @Override
          public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest request, ServerHttpResponse response) {
              // String類型不能直接包裝
              if (returnType.getGenericParameterType().equals(String.class)) {
                  ObjectMapper objectMapper = new ObjectMapper();
                  try {
                      // 將數據包裝在ResultVo里后轉換為json串進行返回
                      return objectMapper.writeValueAsString(Response.success(data));
                  } catch (JsonProcessingException e) {
                      e.printStackTrace();
                  }
              }
             
              // 其他所有結果統一包裝成Response返回
              return Response.success(data);
          }
      }

      我們以test接口為例,test接口原本返回的是String,而toint返回的是Integer

      @RestController
      @RequestMapping("dataserver/{version}/account")
      @ApiVersion(1)
      public class AccountOneController {
      
          @GetMapping("/test")
          public String test() {
              return "測試接口,版本1";
          }
      
         @GetMapping("/toint")
          public Integer toint() {
              return 1;
          }
      
      }

      但是頁面返回是JSON字符串和返回體:

      文檔:調試維護API利器—Swagger

      接口開發完成,調試時,大多數都是使用Postman模擬請求調試或者直接用前端代碼調用調試,其實這兩種都比較麻煩,尤其面對復制參數時,Postman要逐個接口的錄入,十分費事,其實這里可以在SpringBoot中引入Swagger接口文檔組件,接口文檔和接口調試一并解決。

      引入依賴

      直接在maven中引入相關依賴:

           <!-- swagger 3 -->
              <dependency>
                  <groupId>io.springfox</groupId>
                  <artifactId>springfox-boot-starter</artifactId>
                  <version>3.0.0</version>
              </dependency>
              <dependency>
                  <groupId>com.github.xiaoymin</groupId>
                  <artifactId>knife4j-spring-boot-starter</artifactId>
                  <version>3.0.3</version>
              </dependency>

      標準的swagger3引入以上兩個依賴即可,相關版本可自行選擇 

      裝配配置類

      下面進行swagger的配置類和一些swagger相關頁面的配置

      @Configuration
      public class SwaggerConfig {
      
      
          @Bean
          public Docket testApis(){
              return new Docket(DocumentationType.OAS_30)
                      .apiInfo(apidoc())
                      .select()
                      .apis(RequestHandlerSelectors.basePackage("net.gcc.webrestapi.controller"))
                      .paths(PathSelectors.any())
                      .build()
                      .groupName("測試服務")
                      .enable(true);
          }
      
          private ApiInfo apidoc(){
              return new ApiInfoBuilder()
                      .title("測試接口")
                      .description("接口文檔")
                      .contact(new Contact("GCC", "#", "XXXX"))
                      .version("1.0")
                      .build();
          }
      }

      使用注解

      Swagger相關注解明細

      注解 使用位置 作用
      @Api 作用在類上,Controller 表示對類的說明,通常用于描述 Controller 的作用和分類,如 @Api(tags = "用戶管理"),后續會在Swagger文檔中以目錄形式展示
      @ApiOperation 作用在方法上,一般為Controller中具體方法 描述 API 接口的具體操作和功能,例如 @ApiOperation(value = "獲取用戶列表", notes = "根據條件獲取用戶列表") ,在swagger文檔中以目錄內容體現
      @ApiModel 作用于類上,一般是參數實體類 表示這是一個模型類,通常與 @ApiModelProperty 結合使用來描述模型屬性 。
      @ApiModelProperty 用于模型類的屬性上,參數類的成員變量 描述屬性的信息,如 @ApiModelProperty(value = "用戶名", required = true)
      @ApiImplicitParams@ApiImplicitParam 用于方法上,一般為Controller中具體方法 描述接口的隱含參數,例如請求參數或請求頭信息
      @ApiResponses@ApiResponse 用于方法上,一般為Controller中具體方法 描述接口的響應信息,可以指定不同的響應狀態碼和對應的描述信息 。
      @ApiIgnore 用于類或方法上 表示忽略該類或方法,不將其顯示在Swagger文檔中。

      @Api@ApiOperation使用

      @RestController
      @RequestMapping("/dataserver/{version}/manage")
      @Api(tags = "數據源管理服務", description = "用于管理數據源信息")
      @ApiVersion
      public class DataServerController {
      
          @PostMapping("/search")
          @ApiOperation(summary = "分頁查詢數據源")
          public IPage<DataSourceEntity> searchData(@RequestBody SearchParam param){
              //XXXX邏輯
              return new IPage<DataSourceEntity>();
          }
          
          
      }

      @ApiMode@ApiModelProperty

      @Data
      @ApiModel(value = "基礎參數")
      public class BaseParam implements Serializable {
      
          @ApiModelProperty(value = "關鍵字", required = true)
          @NotNull(message = "必須包含關鍵字")
          private String  keyFilter;
          @ApiModelProperty(value = "頁碼", required = true)
          @Min(value = 1,message = "頁碼不可小于1")
          private int pageNo;
          @ApiModelProperty(value = "每頁大小", required = true)
          @Max(value = 100,message = "考慮性能問題,每頁條數不可超過100")
          private int pageSize;
      
      }

      @ApiImplicitParams@ApiImplicitParam

      與ApiMode和ApiModeProperty功能一致,一般用于get請求中的參數描述

         @GetMapping("/extend")
          @ApiOperation(value = "賬號角色",notes = "測試版本1延申接口")
          @ApiImplicitParams({
                  @ApiImplicitParam(value = "accountId",name = "賬號ID"),
                  @ApiImplicitParam(value = "role",name = "角色")
          }
          )
          public String extendTest(String accountId,String role) {
              return new JSONObject().set("account",accountId).set("role",role).toJSONString(0);
          }

      效果

      使用swagger后,直接在頁面訪問 http://127.0.0.1:8080/XXX/doc.html即可訪問接口頁面

       

      不要復雜的postman調用,本地調試可以直接使用調試功能 

      補充:完整的Controller類代碼模板

      @RestController
      @RequestMapping("/dataserver/{version}/manage")
      @Api(tags = "數據源管理服務V1")
      @ApiVersion
      public class DataServerController {
      
          @PostMapping("/search")
          @ApiOperation(value = "分頁查詢數據源",notes = "測試")
          public PageVo<DataSourceVo> searchData(@RequestBody BaseParam param){
              //XXXX邏輯
              return new PageVo<DataSourceVo>();
         }
      
          //get請求,使用ApiImplicitParams注解標明參數
          @GetMapping("/searchAccountAndRole")
          @ApiOperation(value = "賬號角色",notes = "查詢賬號角色")
          @ApiImplicitParams({
                  @ApiImplicitParam(value = "accountId",name = "賬號ID"),
                  @ApiImplicitParam(value = "role",name = "角色")
          })
          public String extendTest(String accountId,String role) {
              return new JSONObject().set("account",accountId).set("role",role).toJSONString(0);
          }
      
      }
      
      //部分參數代碼:
      @Data
      @ApiModel
      public class BaseParam implements Serializable {
          @NotNull(message = "必須包含關鍵字")
          @ApiModelProperty("關鍵字過濾")
          private String  keyFilter;
      
          @Min(value = 1,message = "頁碼不可小于1")
          @ApiModelProperty("分頁頁碼")
          private int pageNo;
      
          @Max(value = 100,message = "考慮性能問題,每頁條數不可超過100")
          @ApiModelProperty("分頁每頁條數")
          private int pageSize;
      
      }
      //響應部分代碼
      @Data
      @ApiModel
      public class DataSourceVo implements Serializable {
          @ApiModelProperty("id")
          private String id;
          @ApiModelProperty("數據源名稱")
          private String name;
          @ApiModelProperty("數據源url")
          private String url;
      }
      
      @Data
      @ApiModel
      public class PageVo<V> {
      
          @ApiModelProperty("總數量")
          private int total;
          @ApiModelProperty("具體內容")
          private List<V> rows;
      }

      補充:完整的@RestControllerAdvice類代碼模板

      關于參數驗證的異常處理和統一返回結構,可以使用一個類來完成,以下是完整模板:

      @RestControllerAdvice(basePackages = "net.gcc.webrestapi")
      public class ControllerExceptionAdvice implements ResponseBodyAdvice<Object> {
      
      
          @Override
          public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
              // 返回結構是Response類型都不進行包裝
              return !methodParameter.getParameterType().isAssignableFrom(Response.class);
          }
      
          @Override
          public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest request, ServerHttpResponse response) {
              // String類型不能直接包裝
              if (returnType.getGenericParameterType().equals(String.class)) {
                  ObjectMapper objectMapper = new ObjectMapper();
                  try {
                      // 將數據包裝在ResultVo里后轉換為json串進行返回
                      return objectMapper.writeValueAsString(Response.success(data));
                  } catch (JsonProcessingException e) {
                      e.printStackTrace();
                  }
              }
              //系統特殊錯誤
              if(data instanceof LinkedHashMap
                      && ((LinkedHashMap<?, ?>) data).containsKey("status")
                      && ((LinkedHashMap<?, ?>) data).containsKey("message")
                      &&((LinkedHashMap<?, ?>) data).containsKey("error")){
                  int code = Integer.parseInt(((LinkedHashMap<?, ?>) data).get("status").toString());
                  String mssage = ((LinkedHashMap<?, ?>) data).get("error").toString();
                  return new Response<>(code,mssage,null);
              }
              // 其他所有結果統一包裝成Response返回
              return Response.success(data);
          }
      
      
          @ExceptionHandler({MethodArgumentNotValidException.class})
          public Response MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e)
          {
              // 默認統一返回響應體,填寫參數錯誤編碼, 從異常對象中拿到錯誤信息
              return new Response(401,e.getBindingResult().getAllErrors().get(0).getDefaultMessage(),"");
          }
      
          //HttpMessageNotReadableException異常為webJSON解析出錯
          @ExceptionHandler({HttpMessageNotReadableException.class})
          public Response HttpNotReqadableExceptionHandler(HttpMessageNotReadableException e)
          {
              // 默認統一返回響應體,填寫參數錯誤編碼, 從異常對象中拿到錯誤信息
              return new Response(401,"參數解析錯誤","");
          }
          
      }

       

      posted @ 2024-08-13 08:34  糖拌西紅柿  閱讀(2325)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 99国精品午夜福利视频不卡99 | 一区二区三区无码免费看| 另类图片亚洲人妻中文无码| 在线精品国精品国产不卡| www欧美在线观看| 日韩伦理片| 欧美牲交a免费| 国产av一区二区午夜福利| 麻豆国产传媒精品视频| 日本欧美一区二区三区在线播放 | 亚洲精品一区二区天堂| 亚洲成人av免费一区| 久久99精品久久久久麻豆| 无码av最新无码av专区 | 激,情四虎欧美视频图片| 国产精品视频中文字幕| 与子敌伦刺激对白播放| 人妻激情偷一区二区三区| 日本一区二区三区专线| 国产精品中文字幕视频| 欧美三级欧美成人高清| 欧美日本在线| 中文字幕在线日韩| 成年入口无限观看免费完整大片| 日韩高清在线亚洲专区国产 | 国产亚洲精品久久久久久久久| 久久精品亚洲中文无东京热| 亚洲国产欧美在线看片一国产 | 老熟妇仑乱换频一区二区| 亚洲欧洲日韩国内精品| 挺进粗大尤物人妻中文字幕| 久久夜色精品国产噜噜亚洲sv| 国产在线观看网址不卡一区| 97色伦97色伦国产| 久久中文骚妇内射| 国产精品免费第一区二区| 成人3d动漫一区二区三区| 久久综合亚洲鲁鲁九月天| 狠狠色狠狠色五月激情| 国产私拍大尺度在线视频 | 久热这里只国产精品视频|