注册
持久层
通过mybatis操作数据库
sql语句
1.用户注册,相当于插入
1
| insert into t_user(username,password,phone_num)values()
|
2.先查询用户名是否存在
1
| select * from t_user where username=?
|
设计接口和抽象方法
定义Mapper接口
创建一个Mapper包
创建UserMapper接口,定义sql的抽象方法
1 2 3 4 5 6 7 8 9 10 11 12
| package com.cy.store.mapper;
import com.cy.store.pojo.User; import org.apache.ibatis.annotations.Mapper;
public interface UserMapper { Integer insert(User user); User findByUsername(String username); }
|
1
| @MapperScan("com.cy.store.mapper")
|
编写映射
1.定义xml映射文件,与接口关联,所有映射文件在resources,创建一个mapper文件夹,存放Mapper的映射文件.
2.创建接口对应的映射文件
1 2 3 4 5 6 7 8
| <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.store.mapper.UserMapper"> </mapper>
|
3.配置sql语句,借助标签完成,insert/update/delete/select
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.store.mapper.UserMapper">
<resultMap id="userEntityMap" type="com.cy.store.pojo.User">
</resultMap>
<insert id="insert" useGeneratedKeys="true" keyProperty="id"> INSERT INTO t_user(username,password,salt,email) VALUES { #{username},#{password},#{salt},#{email} } </insert> <select id="findByUsername" resultType="UserEntityMap"> SELECT * FROM t_user WHERE username = #{username} </select> </mapper>
|
4.将mapper注册到yml里
5.单元测试:每个独立层编写完成编写单元测试方法,来测试功能.在test下创建mapper包
业务层
规划异常
1.RuntimeException,作为异常的子类,再定义个异常类来继承.
业务层异常基类,ServiceException异常,继承RuntimeException异常.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package com.cy.store.service.ex;
public class ServiceException extends RuntimeException{ public ServiceException() { super(); }
public ServiceException(String message) { super(message); }
public ServiceException(String message, Throwable cause) { super(message, cause); }
public ServiceException(Throwable cause) { super(cause); }
protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
|
根据业务层不同的功能来定义异常类型,统一去继承
2.用户在进行时产生用户名被占用的错误,抛出异常:UsernameDuplicstedException异常.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package com.cy.store.service.ex;
public class UsernameDuplicstedException extends ServiceException{
public UsernameDuplicstedException() { super(); }
public UsernameDuplicstedException(String message) { super(message); }
public UsernameDuplicstedException(String message, Throwable cause) { super(message, cause); }
public UsernameDuplicstedException(Throwable cause) { super(cause); }
protected UsernameDuplicstedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
|
3.正在执行数据操作时数据库宕机.处于正在插入时异常:InsertException
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package com.cy.store.service.ex;
public class InsertException extends ServiceException{ public InsertException() { super(); }
public InsertException(String message) { super(message); }
public InsertException(String message, Throwable cause) { super(message, cause); }
public InsertException(Throwable cause) { super(cause); }
protected InsertException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
|
设计接口和抽象方法
在service包下创建UserService接口.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.cy.store.service;
import com.cy.store.pojo.User;
public interface UserService {
void reg(User user);
}
|
2.创建一个实现类UserServiceImpl,需要实现这个接口,并抽象方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| package com.cy.store.service.impl;
import com.cy.store.mapper.UserMapper; import com.cy.store.pojo.User; import com.cy.store.service.UserService; import com.cy.store.service.ex.InsertException; import com.cy.store.service.ex.UsernameDuplicstedException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils;
import java.util.UUID;
@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public void reg(User user) { String username = user.getUsername(); User result = userMapper.findByUsername(username); if(result != null){ throw new UsernameDuplicstedException("用户名被占用");
} String oldPassword = user.getPassword(); String salt = UUID.randomUUID().toString().toUpperCase(); String md5Password = getMD5Password(oldPassword,salt); user.setPassword(md5Password); user.setSalt(salt); Integer rows = userMapper.insert(user); if(rows != 1){ throw new InsertException("用户注册过程中产生未知异常");
} }
private String getMD5Password(String password,String salt){ for(int i = 0;i < 3;i++){ password = DigestUtils.md5DigestAsHex((salt+password+salt).getBytes()).toUpperCase(); } return password; } }
|
3.在`单元测试包下常见UserServiceTests类,加入单元测试
控制层
创建响应
状态码,状态描述信息,数据封装在一个类里,并作为返回值返回给前端
1 2 3 4 5 6 7 8 9 10 11 12
|
public class JasonResult<E> implements Serializable { private Integer state; private String message; private E data; }
|
设计请求
1 2 3 4 5
| 请求路径: users/reg 请求参数: User user 请求类型: post 响应结果: JavaResult<void>
|
处理请求
1.创建控制层对应类UserController类,依赖于业务层接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package com.cy.store.controller;
import com.cy.store.pojo.User; import com.cy.store.service.UserService; import com.cy.store.service.ex.InsertException; import com.cy.store.service.ex.UsernameDuplicstedException; import com.cy.store.util.JasonResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController;
@RestController @RequestMapping("users") public class UserController { @Autowired private UserService userService; @RequestMapping("reg") public JasonResult<Void> reg(User user){ JasonResult<Void> result =new JasonResult<>(); try { userService.reg(user); result.setState(200); result.setMessage("注册成功"); } catch (UsernameDuplicstedException e) { result.setState(4000); result.setMessage("用户名被占用"); } catch (InsertException e) { result.setState(5000); result.setMessage("注册时产生未知异常"); } return result; } }
|
控制层优化设计
在控制层抽象一个父类,统一处理关于异常的操作,编写BaseController类,统一处理异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package com.cy.store.controller;
import com.cy.store.service.ex.InsertException; import com.cy.store.service.ex.ServiceException; import com.cy.store.service.ex.UsernameDuplicstedException; import com.cy.store.util.JasonResult; import org.springframework.web.bind.annotation.ExceptionHandler;
public class BaseController { public static final int OK = 200;
@ExceptionHandler(ServiceException.class) public JasonResult<Void> handleException(Throwable e){ JasonResult<Void> result = new JasonResult<>(e); if(e instanceof UsernameDuplicstedException){ result.setState(4000); result.setMessage("用户名被占用"); }else if (e instanceof InsertException){ result.setState(5000); result.setMessage("注册时产生未知异常"); } return result; } }
|
重构reg()
1 2 3 4 5 6 7 8 9 10
| @RequestMapping("reg") public JasonResult<Void> reg(User user){ JasonResult<Void> result =new JasonResult<>();
userService.reg(user);
return new JasonResult<>(OK); }
|
前端页面
1.在这个页面中编写发送请求的方法,点击事件来完成.选中对应的按钮($(“选择器”)),再去添加点击事件,$.ajax()发送异步请求
2.JQUery封装一个函数,称为$.ajax(),通过对象调用ajax()函数,异步加载相关请求.依靠XHR(XmlHttp Response)
3.ajax()使用方式,需要传递方法作为方法参数,一对大括号称为方法体,
ajax接收多个参数,要求以”,”分割,每组参数以”:”分割,参数组成部分为参数名称(固定的),是参数的值,用字符串标识,无顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| $.ajax({ url: "", type: "", data: "", dataType: "", sucess: function(){ }, error: function(){ } });
|
| 参数 |
功能描述 |
| url |
标识请求地址ex : url: “localhost:8080/users/reg” |
| type |
请求类型,ex: type:”POST” |
| data |
提交的数据 data: “username=tom&pwd=123456” |
| datatype |
数据类型,一般为json dataType: “json” |
| sucess |
服务器正常响应时调用sucess方法,并将服务器返回的数据传递到这个方法 |
| error |
不正常时调用error |
|
|
- js代码可以独立声明在一个js文件中或script标签中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <script type="text/javascript"> $("#btn-reg").click(function (){ let username = $("#username").val(); let passwd = $("#passwd").val(); let mail = $("#email").val(); $.ajax({ url: "/users/reg", type: "POST", data: "username="+username+"&password="+passwd+"&mail="+mail, dataType: "JSON", success: function (json){ if(json.state == 200) { alert("注册成功"); }else { alert("注册失败"); } }, error: function (){ alert("注册时产生未知错误!"+xhr.status); } }); }); </script>
|
用户登录
用户输入用户名密码,将数据交给后台查询,若查到登陆成功,条状到主页
持久层
规划sql语句
密码比较在业务层
1 2
| select * from t_user where username = ?
|
某一个功能模块已经被开发完成,省略当前开发步骤
接口设计与方法
业务层
规划异常
1.用户名对应密码错误,密码匹配错误: PasswordNotMatchException异常,运行时,业务异常
2.用户名没有被找到,抛出异常:UsernameNotFoundException.
3.异常的编写:
业务层继承ServiceException异常类
在具体异常类中定义构造方法
设计业务层接口和抽象方法
1.直接在UserService接口编写抽象方法login(username,password), 将当前数据以当前用户对象形式返回.
状态管理,将数据保存在cookie或session中,避免重复多次请求
id存放在session中,头像在cookie
2.需要在实现类中实现父接口抽象方法
3.测试
抽象方法实现
控制层
处理异常
业务层抛出的异常是什么,需要在统一异常处理类中统一捕获,处理.已经处理过不必添加
1 2 3 4 5 6 7 8
| else if (e instanceof UsernameNotFoundException){ result.setState(5001); result.setMessage("用户数据不存在异常"); }else if (e instanceof PasswordNotMatchException){ result.setState(5002); result.setMessage("用户名密码错误异常"); }
|
设计请求
1 2 3 4 5
| 请求路径: users/login 请求参数: String username,String password 请求类型: post 响应结果: JavaResult<void>
|
处理请求
在UserControl中编写方法
前端页面
用户会话session
session存储在服务器端,保存临时数据对象,在整个项目都可以访问,把session看作一个共享的数据.首次登录获取的用户数据,转移到session.
session.getAttrbute(“key”)获取session数据进行封装,在BaseController.
1.封装session对象中数据获取(父类)和设置(当用户登陆成功后进行数据设置,全局session对象).
2.在父类中封装两个数据:获取uid和username对应两个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
protected final Integer getidFromSession(HttpSession session){ return Integer.valueOf(session.getAttribute("id").toString()); }
protected final String getUsernameFromSession(HttpSession session){ return session.getAttribute("username").toString(); }
|
3.在登录中将数据封装在session对象中,服务器自动创建session.
SpringBoot直接使用session,直接将HttpSession类型对象作为请求处理方法参数,自动注入到形参上
拦截器
拦截器:将所有请求统一拦截到拦截器中,可以在拦截器中定义过滤规则,不满足过滤规则,重新打开login.html页面(重定向,转发)
Springboot项目拦截器定义与使用.
springMVC提供HandlerInterceptor表示拦截器
1 2 3 4 5 6 7 8 9 10 11 12
| public interface HandlerInterceptor { default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; }
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { }
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } }
|
1.LoginInterceptor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.cy.store.interceptor;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor;
public class LoginInterceptor implements HandlerInterceptor {
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object obj = request.getSession().getAttribute("id"); if(obj == null){ response.sendRedirect("/web/login.html"); return false; } return true; } }
|
2.添加白名单(login.html/register.html/index.html),添加黑名单
3.注册过滤器:WebMvcConfigure,将用户定义的拦截器注册,定义一个类,实现该接口.存放在config上
1 2 3
| default void addInterceptors(InterceptorRegistry registry) { }
|
4.重定向次数过多