点击【立即注册】按钮发送
http://localhost:20000/regist 请求
注册成功后重定向到登录界面:
@Controller
public class registerController {
@PostMapping("/regist")
public String regist(){
return "redirect:/login.html";
}
}
新建一个VO来接收前端表单传过来的数据并使用JSR303来校验:
@Data
public class UserRegisterVo {
@NotEmpty(message = "用户名必须提交")
@Length(min = 6,max = 18,message = "用户名必须是6-18位字符")
private String userName;
@NotEmpty(message = "密码不能为空")
@Length(min = 6,max = 18,message = "密码必须是6-18位字符")
private String passWord;
@Pattern(regexp = "^[1]([3-9])[0-9]{9}$",message = "手机号格式不正确")
private String phone;
@NotEmpty(message = "验证码必须填写")
@Length(min = 6,max = 6,message = "验证码只能是6位")
private String code;
}
在将前端的input框加上name属性和后端vo进行对应以保证后端能成功接收到前端传过来的数据:
<input name="userName" maxlength="20" type="text" placeholder="您的用户名和登录名" >
<input name="passWord" maxlength="20" type="password" placeholder="建议至少使用两种字符组合">
<input class="phone" name="phone" id="phoneNumber" maxlength="20" type="text" placeholder="建议使用常用手机">
<input name="code" maxlength="20" type="text" placeholder="请输入验证码" class="caa">
表单后端校验逻辑:
准备好之后开始写表单后端校验逻辑
标注@Valid
处理校验,使用BindingResult来收集校验信息RedirectAttributes给页面返回数据。
校验的主要逻辑是:如果JSR303校验失败,就使用BindingResult来收集错误信息成一个map集合,并将错误信息通过RedirectAttributes(或者Model)返回给前端,在重新重定向到注册页
@PostMapping("/regist")
public String regist(@Valid UserRegisterVo vo, BindingResult result, RedirectAttributes redirectAttributes){
/**
* 表单后端校验逻辑
*/
if(result.hasErrors()){
//将校验错误信息返回给页面,收集成一个map
Map<String, String> errors = result.getFieldErrors().stream()
.collect(Collectors.toMap(FieldError::getField,FieldError::getDefaultMessage));
redirectAttributes.addFlashAttribute("errors",errors);
//校验出错重定向返回到注 册页
return "redirect:/register";
}
短信验证码校验逻辑:
主要逻辑:通过刚才写的vo收集前端表单传过来的数据能拿到所发送的短信验证码,在之前写发送短信验证码功能的时候已经将验证码放到了redis里了(参考jdmall-auth-service里的loginController)因此,我们只需要将redis里的验证码拿出来并与前端传过来的验证码作比较就可以了。
如果redis里边没有验证码,那肯定是超过了redis里存放的时间,因为一但点击发送验证码按钮必然会将验证码存到redis中。
完整表单验证以及短信验证码校验逻辑代码:
@Controller
public class registerController {
@Autowired
RedisTemplate redisTemplate;
@PostMapping("/regist")
public String regist(@Valid UserRegisterVo vo, BindingResult result, RedirectAttributes redirectAttributes){
/**
* 表单校验逻辑
*/
if(result.hasErrors()){
//将校验错误信息返回给页面,收集成一个map
Map<String, String> errors = result.getFieldErrors().stream()
.collect(Collectors.toMap(FieldError::getField,FieldError::getDefaultMessage));
redirectAttributes.addFlashAttribute("errors",errors);
//校验出错重定向返回到注 册页
return "redirect:/register";
}
/**
* 短信验证码校验逻辑以及开始注册
*/
//1、获取表单验证码
String code=vo.getCode();
//2、在Redis里取出验证码
String s = (String)redisTemplate.opsForValue().get(authConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone());
//判断redis里是否有验证码有的话进行第3步,没有直接进入 redis里的验证码过期后的逻辑
if(!StringUtils.isEmpty(s)){
//3、将vo收集的验证码与redis的验证码作比对
if(code.equals(s.split("_")[0])){
//校验验证码成功后,删除验证码
redisTemplate.delete(authConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone());
//验证码通过,开始真正的注册,远程调用
开始真正的注册功能:远程调用完整代码整合在下边
}else{
//验证码对比错误,校验失败
Map<String,String> errors=new HashMap<>();
errors.put(code,"验证码错误");
redirectAttributes.addFlashAttribute("errors",errors);
return "redirect:/register";
}
}else{
//redis里的验证码过期后的逻辑
Map<String,String> errors=new HashMap<>();
errors.put(code,"验证码已过期");
redirectAttributes.addFlashAttribute("errors",errors);
return "redirect:/register";
}
return "redirect:/login.html";
}
}
都校验完毕之后就开始真正的注册了,注册逻辑写在另一个服务上,最后进行远程调用:
在jdmall-member中的MemberController中写一个映射
@PostMapping("/registMember")
public R register(@RequestBody MemberRegistVo registVo){
try {
memberService.regist(registVo);
}catch (Exception e){
return R.error(15001,"手机号或者用户名已存在");
}
return R.ok();
}
MemberRegistVo 里边收集的是表单传过来的信息和UserRegisterVo一样
注册功能就是将传过来的数据保存到数据库里,就是注意一些细节就行了,直接上代码再解释。
@Override
public void regist(MemberRegistVo registVo) {
MemberEntity memberEntity = new MemberEntity();
//设置默认等级
MemberLevelEntity memberLevelEntity=memberLevelDao.getDefaultLevel();
memberEntity.setLevelId(memberLevelEntity.getId());
//设置其它信息
//检查用户名和手机号是否唯一,为了让controller能感知异常,异常机制
checkPhoneIsWeiYi(registVo.getPhone());
checkUserNameIsWeiYi(registVo.getUserName());
memberEntity.setUsername(registVo.getUserName());
memberEntity.setMobile(registVo.getPhone());
//保存密码并加密:md5盐值加密
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String encoderCode=bCryptPasswordEncoder.encode(registVo.getPassWord());
memberEntity.setPassword(encoderCode);
this.save(memberEntity);
}
@Override
//检查手机号是否唯一
public void checkPhoneIsWeiYi(String phone) {
int mobile = this.count(new QueryWrapper<MemberEntity>().eq("mobile", phone));
if(mobile>0){
//如果存在就刨除异常
throw new RRException("手机号已存在");
}
}
@Override
//检查用户名是否唯一
public void checkUserNameIsWeiYi(String userName) {
int CountUserName = this.count(new QueryWrapper<MemberEntity>().eq("username", userName));
if(CountUserName>0){
//如果存在就抛出异常
throw new RRException("用户名已存在");
}
}
将对应数据插入到数据库并设置其默认信息,注意密码要是用MD5盐值加密,用户名和手机号要有唯一性因此需要检查手机号是否唯一,checkPhoneIsWeiYi()、checkUserNameIsWeiYi()这两个方法实现了。
最终再使用openFeign进行远程调用:
完整注册逻辑代码:
@Controller
public class registerController {
@Autowired
RedisTemplate redisTemplate;
@Autowired
memberFeignService memberFeignService;
@PostMapping("/regist")
public String regist(@Valid UserRegisterVo vo, BindingResult result, RedirectAttributes redirectAttributes){
/**
* 表单校验逻辑
*/
if(result.hasErrors()){
//将校验错误信息返回给页面,收集成一个map
Map<String, String> errors = result.getFieldErrors().stream()
.collect(Collectors.toMap(FieldError::getField,FieldError::getDefaultMessage));
redirectAttributes.addFlashAttribute("errors",errors);
//校验出错重定向返回到注 册页
return "redirect:/register";
}
/**
* 短信验证码校验逻辑以及开始注册
*/
//1、获取表单验证码
String code=vo.getCode();
//2、在Redis里取出验证码
String s = (String)redisTemplate.opsForValue().get(authConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone());
//判断redis里是否有验证码有的话进行第3步,没有直接进入 redis里的验证码过期后的逻辑
if(!StringUtils.isEmpty(s)){
//3、将vo收集的验证码与redis的验证码作比对
if(code.equals(s.split("_")[0])){
//校验验证码成功后,删除验证码
redisTemplate.delete(authConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone());
//验证码通过,开始真正的注册,远程调用
R r = memberFeignService.register(vo);
if(r.getCode()==0){
//远程注册成功
return "redirect:/login.html";
}else{
//远程注册失败
Map<String,String> errors=new HashMap<>();
errors.put("msg",r.getData(new TypeReference<String>(){}));
redirectAttributes.addFlashAttribute("errors",errors);
return "redirect:/register";
}
}else{
//验证码对比错误,校验失败
Map<String,String> errors=new HashMap<>();
errors.put(code,"验证码错误");
redirectAttributes.addFlashAttribute("errors",errors);
return "redirect:/register";
}
}else{
//redis里的验证码过期后的逻辑
Map<String,String> errors=new HashMap<>();
errors.put(code,"验证码已过期");
redirectAttributes.addFlashAttribute("errors",errors);
return "redirect:/register";
}
}
}