iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0
Software Development

掌握Java神器,駕馭SpringBoot猛獸系列 第 19

第19日 用Lombok讓你的雙手解放

  • 分享至 

  • xImage
  •  

昨日建立好MyBatis的安裝並用簡易的範例實現CRUD,但回頭觀看程式碼還有不足之處,首先是沒有對客戶端請求進行驗證,第二個是DTO屬性的封裝,直接透過屬性欄位取值,有可能會誤觸,今天來對程式碼進行重構,除了介紹Lombok的使用外,也會藉由資料驗證的範例,順帶使用「攔截器」來掩飾控制器執行的例外捕獲

實用的 Lombok

不斷的複製貼上看到重複性高的程式碼段落,對頻繁的重複操作感到厭倦,比較常見的情況是在實現外部注入的時候,,會在建構子或方法實現屬性變更方式,為了解決這個問題,Lombok套件提供了相應的解決方案,使用Lombok提供的Annotation,能在編譯時自動為類別屬性添加存取方法,或是生成建構子,將符合條件屬性放入參數中來實現外部注入

老樣子先添加Lombok依賴項目

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<scope>annotationProcessor</scope>
</dependency>

重構讓取得屬性操作更安全

現在回到昨日dto資料夾下撰寫的 Mebmer.java,添加 Lombok提供的Annotation,建立Gatter方法

@NoArgsConstructor
@Getter
public class Member {
    private int id;
    private String account;
    private String password;
    private String name;
}

NoArgsConstructor: NoArgsConstructor表示不用自訂建構子,
Getter: 自動對屬性添加相應get方法

打開 ApiController調整Membmer相關請求

//-- ApiController.java
@PostMapping("/member")
public Map createMember(
        @RequestBody Member member) {
    memberDao.create(member);
    Map<String, Object> responseData = new HashMap<>();
    responseData.put("name", member.getName()); // 修改這行
    return responseData;
}

這裡的mebmer實例在編譯時自動添加getName()方法,是套用了@gatter獲得的額外行為,因為這個特性開發者可以不用另外撰寫相關封裝方法,能減少撰寫部分程式的工,也因為是自動執行,能做到預防人工操作造成的事務

設置前線關卡,驗證使用者輸入的資料

昨日撰寫的API請求尚有不足之處,未驗證客戶端的請求,現在來利用validation套件修正問題,開始先添加相關依賴項目安裝套件

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

驗證器的使用方式,就是在接收資料的物件屬性,添加相應的Annotation,現在找到請求資料的聚合體「MemberDTO」,並在屬性添加相關驗證規則

//-- Member.java
@JsonProperty("id")
private int id;

@JsonProperty("account")
@NotNull(message = "請輸入帳號")
@Length(min = 3, max = 7, message = "帳號需介於 3~7 個字")
private String account;

@JsonProperty("password")
@NotNull(message = "請輸入密碼")
@Length(min = 4, max = 10, message = "密碼需介於 4~10 個字")
private String password;

@JsonProperty("name")
@NotNull(message = "請輸入名稱")
@Length(min = 3, max = 5, message = "名稱需介於 3~5 個字")
private String name;

決定好規則後回到ApiController在要套用的參數主體,追加@Valid注釋,這邊在建立 Member的API作範例,在原本方法的member參數做此異動,調整後結果如下

public Map createMember(
            @Valid @RequestBody Member member
)...其他略

接著打開Postman輸入錯誤資料格式進行測試,成功拋出400錯誤,但有個問題,現在資料有驗證,但異常會直接拋出例外,無法從回傳訊息看出錯誤資訊

用攔截器擷取例外錯誤

對於控制器方法執行過程拋出的異常,或處理完成回傳的資料,Spring Boot提供了攔截器的中介層,用來捕獲執行結果或異常,可以在請求響應至客戶端以前,進行相關處理,現在介紹如何使用ControllerAdvice,將攔截器與控制器進行綁定,並定義相關資料攔截的處理邏輯

首先這裡要先了解,參數的例外錯誤是 MethodArgumentNotValidException,因此實作流程是捕捉例外錯誤 MethodArgumentNotValidException,並根據捕捉到的錯誤類型,取得相關資料進行處理,在依照理想格式回傳至客戶端

建立一個資料夾 exception 並在下方新增 ApiErrorException.java,實際程式碼內容如下

@ControllerAdvice
public class ApiErrorException {

    @ExceptionHandler({ MethodArgumentNotValidException.class })
    public ResponseEntity resourceNotFoundException(
            MethodArgumentNotValidException ex,
            BindingResult badResult) {

        // Response Json Body
        Map<String, Object> basResponse = new HashMap<>();

        // Errors body
        Map<String, String> errors = badResult.getFieldErrors().stream()
                .collect(Collectors.toMap(
                        FieldError::getField,
                        FieldError::getDefaultMessage));

        basResponse.put("status", false);
        basResponse.put("errors", errors);

        return new ResponseEntity(basResponse, HttpStatus.BAD_REQUEST);
    }
}

接著在 ApiController 類別層級添加 @ControllerAdvice,將當前控制器與連結器進行綁定

@RestController
@RequestMapping(path = "/api")
@ControllerAdvice <-添加這個注釋
public class ApiController{
    ...略
}

現在重新打開Postman 在次執行建立Member請求,依照理想格式回傳錯誤結構


上一篇
第18日 用MyBatis來做CRUD
下一篇
第20日 介紹攔截器與過濾器
系列文
掌握Java神器,駕馭SpringBoot猛獸30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言