昨日建立好MyBatis的安裝並用簡易的範例實現CRUD,但回頭觀看程式碼還有不足之處,首先是沒有對客戶端請求進行驗證,第二個是DTO屬性的封裝,直接透過屬性欄位取值,有可能會誤觸,今天來對程式碼進行重構,除了介紹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請求,依照理想格式回傳錯誤結構