前面几篇已经完成了资料库的基本操作跟使用Thymeleaf 呈现页面,接下来才真正要踏入Spring Boot 的世界,我们会围绕在注册、登入与会员中心三个基本功能,从最简单的资料库操作,逐步加上资料验证、例外处理、登入日志等功能。
记得相关的注释在Day 09 - Spring Boot 常用注释(下)都有提到,由於篇幅的关系,这边就不阐述开发时的测试程序,主要讲Controller 和Service 这两层的内容,对测试有兴趣的话可以去看Github。
首先,要在控制器层的类别上新增注释@Controller
,宣告这是SpringMVC 的Controller 物件,才继续往下设定请求的路径,其实在上一篇Day 14 - Spring Boot & Thymeleaf已经建立了显示注册与登入页面的Controller,就在里面新增两个POST 的方法,用来接收注册与登入时的送出的表单。
@Controller
public class MemberAccountController {
@Autowired
private MemberAccountService memberAccountService;
@RequestMapping(value = {"/", "/login"}, method = RequestMethod.GET)
public String login(
@ModelAttribute MemberAccount memberAccount,
@ModelAttribute(value = "MESSAGE") String message) {
return "login";
}
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String doLogin(
@ModelAttribute MemberAccount memberAccount,
HttpSession session,
RedirectAttributes redirectAttributes) {
MemberAccountVO memberAccountVO = memberAccountService.login(memberAccount);
if(memberAccountVO == null) {
String message = memberAccountVO == null ? "帐号或密码错误" : "";
redirectAttributes.addFlashAttribute("MESSAGE", message);
return "redirect:login";
}
session.setAttribute("member", memberAccountVO);
return "redirect:information";
}
@RequestMapping(value = "/register", method = RequestMethod.GET)
public String register(@ModelAttribute MemberAccountVO memberAccountVO) {
return "register";
}
@RequestMapping(value = "/register", method = RequestMethod.POST)
public String doRegister(
@ModelAttribute MemberAccountVO memberAccountVO,
RedirectAttributes redirectAttributes) {
Optional<String> optional = memberAccountService.register(memberAccountVO);
String message = optional.orElse("注册成功");
redirectAttributes.addFlashAttribute("MESSAGE", message);
return "redirect:login";
}
}
业务逻辑层的地方跟持久层都一样,先定义好介面,再撰写实作该介面的具体类别,或许有些人会觉得这样很麻烦,不能直接写个类别吗,这个原因其实并不复杂,推荐可以看一下Java 为什麽在Service层要使用Interface 这篇文章,作者有详细解释及实作讲解为什麽要使用介面。
在Day 06 - MVC 与三层架构已经提过的,三层架构的业务逻辑层与持久层都是对应MVC 架构中的M的部分,在业务逻辑层的介面这边,可以用注解分类方法的用途。
public interface MemberAccountService {
// 业务逻辑
public MemberAccountVO login(MemberAccount memberAccount);
public Optional<String> register(MemberAccountVO memberAccountVO);
// 资料库操作
public MemberAccount findMemberAccountByUsername(String username);
}
在业务逻辑层的实作类别上,记得先新增注释@Service
,宣告这是业务处理类别,再依照逻辑需求使用@Autowired
注入所需要的物件,而实际上这边应该要在每个方法上面都写上很多注解,标明这个方法的用途、引数以及回传值,但这边为了不让文章太长就不写了,就先记得在撰写方法的逻辑前,要列出该方法的逻辑顺序。
@Override
public MemberAccountVO login(MemberAccount memberAccount) {
// TODO Auto-generated method stub
// 检查帐号是否存在
// 使用资料库盐值对输入密码进行加密
// 比对密码是否相等
// 取得对应Member 资料
// 组合资料为MemberAccountVO
return null;
}
@Service
public class MemberAccountServiceImpl implements MemberAccountService {
@Autowired
private MemberAccountDao memberAccountDao;
@Autowired
private MemberService memberService;
private String getMd5Password(String password, String salt) {
// 对password + salt 进行三次加密
String str = password + salt;
for (int i = 0; i < 3; i++) {
str = DigestUtils.md5DigestAsHex(str.getBytes()).toUpperCase();
}
return str;
}
@Override
public MemberAccountVO login(MemberAccount memberAccount) {
// TODO Auto-generated method stub
// 检查帐号是否存在
MemberAccount data = memberAccountDao.findMemberAccountByUsername(memberAccount.getUsername());
if(data == null) return null;
// 使用资料库盐值对输入密码进行加密
String md5Password = getMd5Password(memberAccount.getPassword(), data.getSalt());
// 比对密码是否相等
if(!md5Password.equals(data.getPassword())) return null;
// 取得对应Member 资料
Member member = memberService.findMemberByMa_id(data.getId());
if(member == null) return null;
// 组合资料为MemberAccountVO
MemberAccountVO memberAccountVO = new MemberAccountVO();
memberAccountVO.setUsername(memberAccount.getUsername());
memberAccountVO.setName(member.getName());
return memberAccountVO;
}
@Override
public Optional<String> register(MemberAccountVO memberAccountVO) {
// TODO Auto-generated method stub
// 验证栏位是否填写及格式
if(!ValidFormat.isEmail(memberAccountVO.getUsername())) return Optional.of("帐号必须是Email 格式");
if(!ValidFormat.isPassword(memberAccountVO.getPassword())) return Optional.of("密码必须为长度6~16位码大小写英文加数字");
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();
}
@Override
public MemberAccount findMemberAccountByUsername(String username) {
// TODO Auto-generated method stub
return memberAccountDao.findMemberAccountByUsername(username);
}
}
>>: Day15 Gin's Router And RESTful API
文件 原文文件:Page migration 翻译: Non-LRU 分页迁移 ==========...
<template> <!-- Button trigger modal --&...
一、RPA是什麽? RPA 是 Robotic Process Automation的缩写,简称机器...
枚举就是列出有穷序列的型别 透过enum关键字新增了新的Browser型别在范例中列出了一个组项分别...
案例说明及适用场景 当我们有某一个科目,需要管理他是否还有余额未被处理,这个科目就是所谓的 调节科目...