文章目录

  • 整体功能实现
  • 1. 访问注册页面
  • 功能需求:
  • 功能实现
  • ==th:href="@{/index}==
  • ==th:fragment="header"==
  • ==th:replace="index::header"==
  • 2. 提交注册页面
  • 功能需求
  • 功能实现
  • 2.1 Service层处理用户提交的表单数据
  • 环境、工具类准备
  • ==commons-lang3包导入==-==StringUtils.isBlank(key)==
  • 配置域名
  • 设计注册工具类-==UUID,MD5==
  • server层处理业务逻辑
  • 2.1.验证信息
  • 2.2 信息入库
  • 2.3 发送激活邮件
  • 2.2 Controller层处理注册表单提交与信息反馈
  • 请求方式、接受参数
  • 对请求的接收与响应
  • 2.3 View视图模板页面处理
  • 中间提示跳转页面模板
  • ==th:href="@{${target}}==
  • 注册表单页面模板
  • ==th:action="@{/register}==
  • ==name="userName"==
  • ==th:text="${usernameMsg}"==
  • ==is-invalid==
  • ==th:value=""==
  • 邮件模板页面
  • 测试
  • 3. 激活注册用户
  • 功能需求
  • 功能实现
  • 3.1 service层处理激活业务
  • 设计静态常量接口
  • 设计激活业务
  • 3.2 Controller层接收和响应请求
  • ==path = "/activate/{userId}/{code}"==
  • ==@PathVariable("userId") int userId==
  • 请求接收响应处理逻辑
  • 3.2 View层处理模板页面
  • 测试:


参考牛客网高级项目教程

整体功能实现

  • 对于业务需求比较大的网页,可以按照功能进行拆分设计
  • 也是按照请求来拆解设计

springboot聊天室项目_spring boot

1. 访问注册页面

功能需求:

  • 点击顶部区域内的链接,打开注册页面。
  • 注册页面头部和尾部复用主页的页面
  • 点击首页,能够再次跳转回首页

springboot聊天室项目_spring boot_02

功能实现

  • 只是为了显示表单信息,只需在视图层处理即可,没有modle需要封装
  • 需要处理以下信息:
  • Controller处理点击访问url,直接返回模板页面,因此可以设定为GET请求
@Controller
public class LoginController {
    // 注册页面的显示
    @RequestMapping(path = "/register", method = RequestMethod.GET)
    public String getRegisterPage() {
        return "/site/register";
    }
}
th:href="@{/index}
th:fragment=“header”
th:replace=“index::header”
  • View处理: 将主页的头部和尾部复用,使用th引擎处理
  • url点击位置在index主页上,因此需要处理主页页面的url链接,用th控制
  • th:href="@{/index}
  • th:href="@{/register}"
  • 定义复用片段的别名:th:fragment=“header”
  • 替换要复用的片段:th:replace=“index::header”

2. 提交注册页面

功能需求

  • 用户通过表单提交数据。
  • 服务端验证账号是否已存在、邮箱是否已注册。
  • 注册成功后,
  • 将用户信息添加进数据库
  • 注册成功后,服务端向用户注册邮箱发送激活邮件
  • 注册失败:
  • 将错误信息封装,并在注册表单页面上显示错误提示信息

功能实现

2.1 Service层处理用户提交的表单数据
环境、工具类准备
commons-lang3包导入-StringUtils.isBlank(key)
  • 提供判断字符串、集合常用数据结构空值等其他情况
  • null,空字符串、空格均被判为空值
<!--提供判断字符串、集合常用数据结构空值情况-->
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
   <version>3.9</version>
</dependency>
配置域名
  • 注册邮箱时,需要向用户邮箱发送激活码,激活码中要拼接带有用户id,激活码信息
  • 因此需要将当前项目域名、项目名、功能名拼接进去
  • 提前在配置类中配置好,以便复用
# community,域名
community.path.domain=http://localhost:8080
# url中的项目名
server.servlet.context-path=/community
设计注册工具类-UUID,MD5

可以定义成静态方法,可以直接使用,无需交给SpringIOC容器管理

  • 1.生成随机字符串
  • 将"-" 替换成""
// 生成随机字符串
    public static String generateUUID() {
        return UUID.randomUUID().toString().replace("-", "");
    }
  • 2.MD5加密
  • MD5实质上只能加密,不能解密,
  • 但简单字符串加密后可以破解,是查询了MD5密码库
  • 所以,为了增加安全性,将用户密码拼接上一个salt(随机字符串),再MD5加密,库中就查不到了
  • 调用Spring中自带的MD5加密16进制字符串方法
  • 不过,接受的参数为byte数组类型,需将字符串类型转换
// MD5加密
// hello -> abc123def456
// hello + 3e4a8 -> abc123def456abc
public static String md5(String key) {
    // 判空
    if(StringUtils.isBlank(key)) {
        return null;
    }
    // 调用Spring的工具类
    return DigestUtils.md5DigestAsHex(key.getBytes())
}
server层处理业务逻辑
  1. 数据属性注入
  • 发邮件工具类
  • 模板引擎
  • url的域名、项目名,注意变量名不要用关键字context
@Autowired
private MailClient mailClient;

@Autowired
private TemplateEngine templateEngine;

@Value("${community.path.domain}")
private String domain;

@Value("${server.servlet.context-path}")
private String contextPth;	//
  1. 注册业务方法
  • 返回值:user,用户信息
  • 接受参数:返回的注册错误信息
/**
 * 注册业务
 * @param user,用户信息
 * @return  返回的注册错误信息
 */
public Map<String, Object> register(User user) {
    Map<String, Object> falseMap = new HashMap<>();
    ...
    return falseMap;
}
  • 内部逻辑:
2.1.验证信息
  • 空值处理
  • 账户、密码、邮箱
  • 与数据库比对验证
  • 账户不能重复
  • 邮箱不能重复
Map<String, Object> falseMap = new HashMap<>();
//        1.验证用户输入内容是否有问题
        // 用户对象不能为null
        if(user == null) {
            throw new IllegalArgumentException("注册用户不能为空");
        }

        // 内容不能为空
        // 用户名
        if(StringUtils.isBlank(user.getUsername())) {
            falseMap.put("userNameMsg", "用户名不能为空!");
            return falseMap;
        }
        // 密码
        if(StringUtils.isBlank(user.getPassword())) {
            falseMap.put("passwordMsg", "密码不能为空!");
            return falseMap;
        }
        // 邮箱
        if(StringUtils.isBlank(user.getEmail())) {
            falseMap.put("emailMsg", "邮箱不能为空!");
            return falseMap;
        }
2.2 信息入库
  • 验证无误,将用户信息入库,并向用户发送激活链接
// 先将密码进行MD5加密
String salt = CommunityUtil.generateUUID().substring(0, 5);
user.setSalt(salt);
user.setPassword(CommunityUtil.md5(user.getPassword() + salt));
// 其他状态设置
user.setType(0); // 普通用户
user.setStatus(0); // 未激活
String code = CommunityUtil.generateUUID();
user.setActivationCode(code); // 激活码
user.setCreateTime(new Date()); // 创建时间
user.setHeaderUrl(String.format(    // 用户头像,随机生成
        "http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)));
// 入库
userMapper.insertUser(user);
2.3 发送激活邮件
  • 向注册用户邮箱发邮件
// 创建封装数据的Context,封装数据
Context context = new Context();    // thymeleaf模板中的Context,相当于model
context.setVariable("toMsg", user.getEmail());
// 拼接激活链接url,http://localhost:8080/community/activation/101/code
String url = domain + contextPth + "/activate" + "/" + user.getId() + "/" + code;
context.setVariable("urlMsg", url);

// 发送HTML邮件
// 利用模板生成动态网页,需将模板视图传过去
// 模板引擎自动识别context传入的参数,然后动态加载到网页中,
// 将网页的动态变量进行替换,并将网页内容加载到content中
String content = templateEngine.process("/mail/activation", context);
mailClient.sendMail(user.getEmail(), "交流网用户注册激活链接", content);
  • 处理激活邮件模板页面
  • 渲染链接:
  • 1.th:href="@{${urlMsg}}"
  • 2.th:href="${urlMsg}" 都行,但通过Controller传过去数据只能用第二种
<div>
   <p>
      <b th:text="${toMsg}">xxx@xxx.com</b>, 您好!
   </p>
   <p>
      您正在注册交流网, 这是一封激活邮件, 请点击
      <a th:href="@{${urlMsg}}">此链接</a>,
      激活您的社交账号!
   </p>
</div>

2.2 Controller层处理注册表单提交与信息反馈

请求方式、接受参数
  • 提交表单数据,用Post请求,路径与GET请求一样,但,请求方式不同,处理方式不同
  • 直接用User类接收表单数据
  • 属性名相同,Spring会自动将表单中对应属性值userName,password,email等属性值注入到user类中
// 注册页面表单提交请求
    @RequestMapping(path = "/register", method = RequestMethod.POST)
    public String register(Model model, User user) {
        ...
        return "";
    }
对请求的接收与响应

接收提交表单数据,交给service处理后,返回给表单

  • 接收的user类中表单输入信息交给service层处理后,获取返回的map信息
  • 处理map信息,
  • 成功,封装激活中间页面需要的model信息:
  • 页面显示的提示信息和最终跳转的链接:
  • msg:提示注册成功,已经向用户发送了激活邮件,提醒用户尽快激活账户
  • target:一定时间后,自动跳转或立即手动跳转的指定链接
  • 跳转到激活中间页面
  • 失败,封装map错误信息,
  • 重新返回注册页面,并显示错误提示信息
// 注册页面表单提交请求
@RequestMapping(path = "/register", method = RequestMethod.POST)
public String register(Model model, User user) {
    // 将接收的user类中表单输入信息交给service层处理后,获取返回的map信息
    Map<String, Object> falseMap = userService.register(user);
    
    // 前端显示处理这些信息
    if(falseMap == null || falseMap.isEmpty()) {
        return "/site/operate-result"; // 注册成功跳转到激活提示激活页面
    } else {
        model.addAttribute("userNameMsg", falseMap.get("userNameMsg"));
        model.addAttribute("passwordMsg", falseMap.get("passwordMsg"));
        model.addAttribute("emailMsg", falseMap.get("emailMsg"));
        return "/site/register"; // 返回注册表单页面
    }
}

2.3 View视图模板页面处理

中间提示跳转页面模板
th:href="@{${target}}
  • thymeleaf自动识别并动态填入链接的url值
<div class="jumbotron">
   <p class="lead" th:text="${msg}">您的账号已经激活成功,可以正常使用了!</p>
   <hr class="my-4">
   <p>
      系统会在 <span id="seconds" class="text-danger">8</span> 秒后自动跳转,
      您也可以点此 <a id="target" th:href="@{${target}}" class="text-primary">链接</a>, 手动跳转!
   </p>
</div>
注册表单页面模板
th:action="@{/register}
  • 处理表头:method=“post” th:action="@{/register}
<form class="mt-5" method="post" th:action="@{/register}">
name=“userName”
  • 处理表单中的每行标签内容:html中Input标签中,属性名一定要和User类中的属性名一致
  • 这样,Spring才能自动识别并注入数据到类中
th:text="${usernameMsg}"
is-invalid
  • 显示错误提示信息,将controller返回的model封装的错误信息渲染处理
  • 如果没有msg错误信息,则不显示,Bootstrap前端框架处理是在input标签的class选择器中处理
  • 因此需要"|静态 动态|"拼接,显示样式均交给css处理,放在class选择器中
  • Bootstrap中判断:is-invalid,输入内容非法,就将错误提示框显示出来,反之不显示
<input type="text"
      th:class="|form-control ${usernameMsg != null ? 'is-invalid' : ''}|"
      th:value="${user != null ? user.userName : ''}"
      id="username" name="userName" placeholder="请输入您的账号!" required>
<div class="invalid-feedback" th:text="${userNameMsg}">
   该账号已存在!
</div>
th:value=""
  • 默认值的显示,如果反馈注册信息,将之前填入的值自动填入
  • 要动态判断,如果第一次访问没有默认值
  • user为null,再调用user的属性方法,会异常,故要判空处理
th:value="${user != null ? user.userName : ''}"
邮件模板页面
  • 用thymeleaf模板引擎处理的,因此在service层处理完业务后,就处理模板页面,免得遗忘了
<div>
   <p>
      <b th:text="${toMsg}">xxx@xxx.com</b>, 您好!
   </p>
   <p>
      您正在注册交流网, 这是一封激活邮件, 请点击
      <a th:href="@{${urlMsg}}">此链接</a>,
      激活您的社交账号!
   </p>
</div>

测试

  • 结果显示

springboot聊天室项目_java_03

springboot聊天室项目_表单_04

springboot聊天室项目_表单_05

3. 激活注册用户

功能需求

  • 点击邮件中的链接,访问服务端的激活服务
  • 将用户激活状态改变,并在页面显示激活的结果

功能实现

3.1 service层处理激活业务
设计静态常量接口
  • 设置项目中的静态常量接口,可以直接使用静态常量来表示激活信号
public interface CommunityConstant {
    /**
     * 激活成功
     */
    int ACTIVATION_SUCCESS = 0;

    /**
     * 重复激活
     */
    int ACTIVATION_REPEAT = 1;

    /**
     * 激活失败
     */
    int ACTIVATION_FAILURE = 2;
}
设计激活业务
  • 将从controller接收的用户id从库中查询指定用户
  • 根据指定用户的激活码与传入的激活码比对,处理不同结果
  • 激活成功:返回成功信号,并将指定用户的状态更新为已激活状态,static=1
  • 激活失败:
  • 用户已经激活,重复激活
  • 激活码不对,不能激活
/**
 * 处理用户激活业务
 * @param userId 接收Controller解析url中的userID,定位指定用户
 * @param code  接收Controller解析url中的激活码
 * @return      返回激活状态,用静态变量展示
 */
public int activation(int userId, String code) {
    // controller接收的用户id从库中查询指定用户
    User user = userMapper.selectById(userId); 
    // 指定用户的激活码与传入的激活码比对
    if (user.getStatus() == 1) {   // 重复激活
        return ACTIVATION_REPEAT;
    } else if (user.getActivationCode().equals(code)) { // 激活码匹配成功
        userMapper.updateStatus(userId, 1); // 更新用户状态,激活成功
        return ACTIVATION_SUCCESS;
    } else {    // 激活码不匹配,不能激活
        return ACTIVATION_FAILURE;
    }
}
3.2 Controller层接收和响应请求
path = “/activate/{userId}/{code}”
  • 请求路径:为之前发送到邮件的激活链接url,RestFul风格拼接的字符串
  • 请求方式为Get请求,浏览器直接访问POST请求不被支持
@PathVariable(“userId”) int userId
  • 解析RestFul风格拼接的字符串中后面拼接的路径名的值
// http://localhost:8080/community/activation/101/code
@RequestMapping(path = "/activate/{userId}/{code}", method = RequestMethod.GET)
public String activation(Model model, 
                         @PathVariable("userId") int userId, 
                         @PathVariable("code") String code) {
    ...
    return "/site/operate-result";
}
请求接收响应处理逻辑
  • 接收用户传入的url,并将路径解析
  • 将userId,code解析出来后交给service处理
  • 接收service反馈的信息
  • 将信息转为跳转中间页面需要的msg、target封装,交给中间页面渲染显示给用户浏览器
// http://localhost:8080/community/activation/101/code
@RequestMapping(path = "/activate/{userId}/{code}", method = RequestMethod.GET)
public String activation(Model model, 
                         @PathVariable("userId") int userId, 
                         @PathVariable("code") String code) {
    // 将userId,code解析出来后交给service处理,
    // 接收service反馈的信息
    int result = userService.activation(userId, code);
    if (result == ACTIVATION_SUCCESS) { // 激活成功,跳转到登录页面
        model.addAttribute("msg", "激活成功,您的账号已经可以正常使用了,请登录!");
        model.addAttribute("target", "/login");
    } else if (result == ACTIVATION_REPEAT) { // 重复激活
        model.addAttribute("msg", "无效操作,该账号已经激活过了!");
        model.addAttribute("target", "/index");
    } else {
        model.addAttribute("msg", "激活失败,您提供的激活码不正确!");
        model.addAttribute("target", "/index");
    }
    return "/site/operate-result";
}
3.2 View层处理模板页面
  • 主要处理激活成功后要跳转的登录模板页面
  • 加上thymeleaf模板引用即可
  • 中间跳转模板页面,前面注册时已经处理好了

测试: