iT邦幫忙

2021 iThome 鐵人賽

DAY 16
1

在上一篇Day 15 - Spring Boot 註冊與登入中,我們已經初步完成了註冊與登入的基礎功能,但我們要記得,Spring Boot 為了簡化開發的流程提供了許多開箱即用的依賴,這篇我們就要介紹Spring Boot 提供關於資料驗證的依賴,也就是Day 07 - Spring Boot 常用依賴中提到spring-boot-starter-validation,主要透過註釋的方式進行,可以進一步簡化我們的程式碼,讓我們更專注在業務邏輯上。

常用註釋

當然,不免俗的要先看一下它提供的註釋,以下列出一些常用的註釋,可以不用背下來,但要記得有關於資料驗證的功能,可以先查一下它有沒有提供相關的註釋。

  • @Valid : 註釋在方法中要校驗的參數上,用於啟動校驗,可搭配BindingResult 物件獲取校驗失敗的訊息;也可以註釋在成員屬性上,用於進行嵌套驗證。
  • @Validated : 對於@Valid 的封裝,由Spring 提供的校驗機制,比@Valid 多了分組功能,但必須配合@Valid 才能進行嵌套驗證。
  • @Email : 被註釋的屬性必須是電子信箱格式
  • @Pattern : 被註釋的屬性必須符合指定的正則表示法
  • @Null : 被註釋的屬性必須為Null
  • @NotNull : 被註釋的屬性必須不為Null
  • @NotEmpty : 被註釋的字串不可為空。
  • @NotBlank : 被註釋的字串非null 且長度必須大於0。
  • @Length : 被註釋的字串長度必須在指定的範圍內。
  • @Min(value) : 被註釋的屬性必須為一個數字且大於或等於指定的最小值。
  • @Max(value) : 被註釋的屬性必須為一個數字且小於或等於指定的最大值。
  • @DecimalMin(value) : 被註釋的屬性必須為一個數字且大於或等於指定的最小值。
  • @DecimalMax(value) : 被註釋的屬性必須為一個數字且小於或等於指定的最大值。
  • @Size(max, min) : 被註釋的屬性必須為一個數字且在指定的範圍內。
  • @Digits(integer, fraction) : 被註釋的屬性必須為一個數字且在可接收的範圍內。
  • @Range : 被註釋的屬性必須在合適的範圍內。
  • @AssertTrue : 被註釋的屬性必須為一個布林值且為true。
  • @AssertFalse : 被註釋的屬性必須為一個布林值且為false。
  • @Past : 被註釋的屬性必須為一個過去的日期
  • @Future : 被註釋的屬性必須為一個未來的日期

實作

新增依賴

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

修改MemberAccount 實體類

package com.example.demo.entity;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class MemberAccount extends Base {

	private String id;

	@Email(message = "帳號必須是Email 格式")
	@NotBlank(message = "帳號不可為空")
	private String username;

	@NotBlank(message = "密碼不可為空")
	@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[\\w]{6,16}$", 
			 		 message = "密碼必須為長度6~16位碼大小寫英文加數字")
	private String password;
	
	private String salt;

}

修改控制器層

@Controller
public class MemberAccountController {

	// 略...

	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public String doLogin(
			@Valid @ModelAttribute MemberAccount memberAccount,
			HttpSession session, 
			RedirectAttributes redirectAttributes) {
		
		MemberAccountVO memberAccountVO = memberAccountService.login(memberAccount);
		if(memberAccountVO == null) {
			redirectAttributes.addFlashAttribute("MESSAGE", "帳號或密碼錯誤");
			return "redirect:login";
		}
		session.setAttribute("member", memberAccountVO);	
		return "redirect:information";
	}

	// 略...
	
	@RequestMapping(value = "/register", method = RequestMethod.POST)
	public String doRegister(
			@Valid @ModelAttribute MemberAccountVO memberAccountVO,
			RedirectAttributes redirectAttributes) {

		Optional<String> optional = memberAccountService.register(memberAccountVO);
		String message = optional.orElse("註冊成功");
		redirectAttributes.addFlashAttribute("MESSAGE", message);
		return "redirect:login";
	}
	
}

修改業務邏輯層的實作

可以看到註冊的業務邏輯,少了前面對帳號及密碼格式的判斷,別小看這邊只少了兩行,當專案越來越大,搭配@Validated 為驗證邏輯進行分組,可以減少各處的驗證判斷。

@Service
public class MemberAccountServiceImpl implements MemberAccountService {

	// 略...

	@Override
	public Optional<String> register(MemberAccountVO memberAccountVO) {
		// TODO Auto-generated method stub
		// 檢查兩次輸入密是否相符
		if(!memberAccountVO.getPassword().equals(memberAccountVO.getCheckPassword())) return Optional.of("兩次輸入密碼不相符");
		
		// 檢查帳號是否重複註冊
		MemberAccount data = memberAccountDao.findMemberAccountByUsername(memberAccountVO.getUsername());
		if(data != null) return Optional.of("該帳號已被使用");
		
		// 產生鹽值
		String salt = UUID.randomUUID().toString().toUpperCase().replaceAll("-", "");
		
		// 密碼加密
		String md5Password = getMd5Password(memberAccountVO.getPassword(), salt);

		// 新增MemberAccount 資料
		MemberAccount memberAccount = new MemberAccount();
		memberAccount.setUsername(memberAccountVO.getUsername());
		memberAccount.setPassword(md5Password);
		memberAccount.setSalt(salt);
		memberAccount.setCreate_by(memberAccountVO.getUsername());
		memberAccount.setUpdate_by(memberAccountVO.getUsername());
		Integer id = memberAccountDao.insert(memberAccount);
		if(id == 0) return Optional.of("新增會員帳號時發生錯誤");

		// 新增Member 資料
		Member member = new Member();
		member.setMa_id(String.valueOf(id));
		member.setName(memberAccountVO.getName());
		member.setCreate_by(memberAccountVO.getUsername());
		member.setUpdate_by(memberAccountVO.getUsername());
		Integer result = memberService.insert(member);
		if(result == 0) return Optional.of("新增會員資料時發生錯誤");
		
		return Optional.empty();
	}

	// 略...

}

Github

新增資料驗證功能

參考網站

使用SpringBoot進行優雅的資料驗證_部落格園精華區 - MdEditor
SpringBoot - 第二十章 | 資料驗證(二)


上一篇
Day 15 - Spring Boot 註冊與登入
下一篇
Day 17 - Spring Boot 例外處理
系列文
誤打誤撞學了Spring Boot 還當了後端工程師30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言