當前位置: 妍妍網 > 碼農

【禁止血壓飆升】如何擁有一個優雅的 Controller?

2024-05-19碼農

來源:juejin.cn/post/7357172505961578511

👉 歡迎 ,你將獲得: 專屬的計畫實戰 / Java 學習路線 / 一對一提問 / 學習打卡 / 每月贈書

新計畫: 仿小紅書 (微服務架構)正在更新中... , 全棧前後端分離部落格計畫 2.0 版本完結啦, 演示連結 http://116.62.199.48/ 。全程手摸手,後端 + 前端全棧開發,從 0 到 1 講解每個功能點開發步驟,1v1 答疑,直到計畫上線。 目前已更新了261小節,累計41w+字,講解圖:1806張,還在持續爆肝中.. 後續還會上新更多計畫,目標是將Java領域典型的計畫都整一波,如秒殺系統, 線上商城, IM即時通訊,Spring Cloud Alibaba 等等,

前言

見過幾千行程式碼的 controller嗎?我見過。

見過全是 try catch 的 controller 嗎,我見過。

見過全是欄位校驗的 controller 嗎,我見過。

見過全是業務程式碼的 controller 嗎?不好意思,我們公司很多業務寫在 controller 的。

看見這些我真的血壓高。

正文

不優雅的 controller

@RestController
@RequestMapping("/user/test")
public class UserController {
private static Logger logger = LoggerFactory.getLogger(UserController. class);
@Autowired
private UserService userService;
@Autowired
private AuthService authService;
@PostMapping
public CommonResult userRegistration(@RequestBody UserVo userVo) {
if (StringUtils.isBlank(userVo.getUsername())){
return CommonResult.error("使用者名稱不能為空");
}
if (StringUtils.isBlank(userVo.getPassword())){
return CommonResult.error("密碼不能為空");
}
logger.info("註冊使用者:{}" , userVo.getUsername());
try {
userService.registerUser(userVo.getUsername());
return CommonResult.ok();
}catch (Exception e){
logger.error("註冊使用者失敗:{}", userVo.getUsername(), e);
return CommonResult.error("註冊失敗");
}
}
@PostMapping("/login")
@PermitAll
@ApiOperation("使用帳號密碼登入")
public CommonResult<AuthLoginRespVO> login(@RequestBody AuthLoginReqVO reqVO) {
if (StringUtils.isBlank(reqVO.getUsername())){
return CommonResult.error("使用者名稱不能為空");
}
if (StringUtils.isBlank(reqVO.getPassword())){
return CommonResult.error("密碼不能為空");
}
try {
return success(authService.login(reqVO));
}catch (Exception e){
logger.error("註冊使用者失敗:{}", reqVO.getUsername(), e);
return CommonResult.error("註冊失敗");
}
}
}




優雅的controller

@RestController
@RequestMapping("/user/test")
public class UserController1 {
private static Logger logger = LoggerFactory.getLogger(UserController1. class);
@Autowired
private UserService userService;
@Autowired
private AuthService authService;
@PostMapping("/userRegistration")
public CommonResult userRegistration(@RequestBody @Valid UserVo userVo) {
userService.registerUser(userVo.getUsername());
return CommonResult.ok();
}
@PostMapping("/login")
@PermitAll
@ApiOperation("使用帳號密碼登入")
public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) {
return success(authService.login(reqVO));
}
}




程式碼量直接減一半呀,這還不算上有些直接把業務邏輯寫在 controller 的,看到這些我真的直接吐血

改造流程

校驗方式

這個 if 校驗看得我哪哪都不爽。好歹給我寫一個斷言吧。 Assert.notNull(userVo.getUsername(), "使用者名稱不能為空");

這不香嗎?確實不香。

使用 spring 提供的@Valid

在入參時使用@Valid註解,並且在 vo 中使用校驗註解,如 AuthLoginReqVO

@ApiModel(value = "管理後台 - 帳號密碼登入 Request VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthLoginReqVO {
@ApiModelProperty(value = "帳號", required = true, example = "user")
@NotEmpty(message = "登入帳號不能為空")
@Length(min = 4, max = 16, message = "帳號長度為 4-16 位")
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "帳號格式為數位以及字母")
private String username;
@ApiModelProperty(value = "密碼", required = true, example = "password")
@NotEmpty(message = "密碼不能為空")
@Length(min = 4, max = 16, message = "密碼長度為 4-16 位")
private String password;
}

@Valid

在SpringBoot中,@Valid是一個非常有用的註解,主要用於數據校驗。以下是關於@Valid的一些詳細資訊:

  • 為什麽使用 @Valid 來驗證參數: 在編寫介面時,我們經常需要驗證請求參數。通常,我們可能會寫大量的 if 和 if else 程式碼來進行判斷。但這樣的程式碼不僅不優雅,而且如果存在大量的驗證邏輯,這會使程式碼看起來混亂,大大降低程式碼可讀性。為了簡化這個過程,我們可以使用 @Valid 註解來幫助我們簡化驗證邏輯。

  • @Valid 註解的作用: @Valid 的主要作用是用於數據效驗,可以在定義的實體中的內容上,添加不同的註解來完成不同的校驗規則,而在介面類中的接收數據參數中添加 @valid 註解,這時你的實體將會開啟一個校驗的功能。

  • @Valid 的相關註解: 在實體類中不同的內容上添加不同的註解,就能實作不同數據的效驗功能。

  • 使用 @Valid 進行參數效驗步驟: 整個過程如下,使用者存取介面,然後進行參數效驗,因為 @Valid 不支持平面的參數效驗(直接寫在參數中欄位的效驗)所以基於 GET 請求的參數還是按照原先方式進行效驗,而 POST 則可以以實體物件為參數,可以使用 @Valid 方式進行效驗。如果效驗透過,則進入業務邏輯,否則丟擲異常,交由全域例外處理器進行處理。

  • @Validated與@Valid的區別: @Validated 是 @Valid 的變體。透過聲明實體中內容的 groups ,再搭配使用 @Validated ,就能決定哪些內容需要校驗,哪些不需要校驗。

  • 全域例外處理

    這個全域例外處理,可以根據自己的異常,自訂例外處理,並設定一個兜底的例外處理

    @ResponseBody
    @RestControllerAdvice
    public class ExceptionHandlerAdvice {
    protected Logger logger = LoggerFactory.getLogger(get class());
    @ExceptionHandler(MethodArgumentNotValidException. class)
    public CommonResult<Object> handleValidationExceptions(MethodArgumentNotValidException ex) {
    logger.error("[handleValidationExceptions]", ex);
    StringBuilder sb = new StringBuilder();
    ex.getBindingResult().getAllErrors().forEach(error -> {
    String fieldName = ((org.springframework.validation.FieldError) error).getField();
    String errorMessage = error.getDefaultMessage();
    sb.append(fieldName).append(":").append(errorMessage).append(";");
    });
    return CommonResult.error(sb.toString());
    }
    /**
    * 處理系統異常,兜底處理所有的一切
    */
    @ExceptionHandler(value = Exception. class)
    public CommonResult<?> defaultExceptionHandler(Throwable ex) {
    logger.error("[defaultExceptionHandler]", ex);
    // 返回 ERROR CommonResult
    return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg());
    }
    }

    就這麽多,搞定,這樣就擁有了漂流優雅的 controller 了

    在日常開發中,還有那些血壓飆升瞬間

    我拿出下圖閣下如何面對

    圖片

    這個閣下又如何面對,我不說,你能知道這個什麽嗎【狗頭】

    圖片

    總結

    不是很明白為什麽有些喜歡在 controller 寫業務邏輯的,曾經有個同事問我(就是喜歡在 controller 寫業務的),你這個介面寫在那裏,我需要調一下你這個介面。我滿臉問號??不是隔壁的模組嗎,為什麽要調我的介面?直接參照的我的 service 去調方法就好了。

    這個就是痛點,各寫各的,冗余程式碼一堆。

    曾經看到一個同事寫一個保存的方法,雖然邏輯挺多,我滑動了好久都還沒有方法還沒有結束。一個方法整整幾百行……

    看過 spring 源碼都知道,spring 源碼難啃,就是因為 spring 無限往下套娃,基本每個方法幹每個方法的事情。比如我保存使用者時,就只是保存使用者,至於什麽校驗丟給校驗的方法處理,什麽發送訊息丟給發送訊息處理,這些就不能耦合在一起。

    對於看到一些 if 下面一丟邏輯,然後 if 再一丟邏輯,看程式碼時很多情況不需要知道這個邏輯怎麽實作的,知道入參出參就大概這裏做什麽了。即使想知道詳細情況點進去就知道了。突出這個當前方法要做的事情就好了。

    阿裏的開發手冊就推薦一個方法不能超過 80 行,超過可以根據業務具體調整一下。

    👉 歡迎 ,你將獲得: 專屬的計畫實戰 / Java 學習路線 / 一對一提問 / 學習打卡 / 每月贈書

    新計畫: 仿小紅書 (微服務架構)正在更新中... , 全棧前後端分離部落格計畫 2.0 版本完結啦, 演示連結 http://116.62.199.48/ 。全程手摸手,後端 + 前端全棧開發,從 0 到 1 講解每個功能點開發步驟,1v1 答疑,直到計畫上線。 目前已更新了261小節,累計41w+字,講解圖:1806張,還在持續爆肝中.. 後續還會上新更多計畫,目標是將Java領域典型的計畫都整一波,如秒殺系統, 線上商城, IM即時通訊,Spring Cloud Alibaba 等等,


    1. 

    2. 

    3. 

    4. 

    最近面試BAT,整理一份面試資料Java面試BATJ通關手冊,覆蓋了Java核心技術、JVM、Java並行、SSM、微服務、資料庫、數據結構等等。

    獲取方式:點「在看」,關註公眾號並回復 Java 領取,更多內容陸續奉上。

    PS:因公眾號平台更改了推播規則,如果不想錯過內容,記得讀完點一下在看,加個星標,這樣每次新文章推播才會第一時間出現在你的訂閱列表裏。

    「在看」支持小哈呀,謝謝啦