up:: SpringBoot电商项目用户模块API统一返回对象
说明:
(1) 本篇博客主要内容:开发注册接口;
(2) 本篇博客涉及到的点:
● Controller层中发现的错误,我们可以直接返回对应错误ApiRestResponse;Service层中发现的错误,我们不能直接返回对应错误的ApiRestResponse,而是采用【向上抛异常】的策略来解决这个问题;
● mybatis中的一个用法:【include refid=“Base_Column_List”/】,即使用include来引用SQL片段;
● 这儿判空时,我们没有使用【userName == null】的方式(因为,有的时候,前端确实传过来了userName,这个userName字符串是个空字符串;那么此时,这种方式就可能出现误判);而是使用的Springframework提供的一个工具【StringUtils.isEmpty(userName)】来判空;
● 我们在【项目初始化创建SpringBoot项目(必看)】中,通过【mybatis-generator】插件生成的方法中,有【userMapper.insertSelective(user)】方法和【insertSelective()】方法;这二者的区别,就是insertSelective()允许插入一个【部分属性有值的,User对象】;
(3) 本篇博客尤其需要注意的两点内容:
●【非Controller层出现的错误】应该采取向上抛异常的策略来处理;这在【SpringBoot电商项目用户模块注册接口开发之自定义异常类】中做了详细阐述;
● 对于抛出的异常也需要进行处理,以达到返回格式统一和安全这两个目的;这在【Spring Boot电商项目用户模块注册接口开发之全局统一处理异常】中做了详细阐述;
零:【注册】接口,预先说明;
注册接口的接口文档:

同时,在【SpringBoot电商项目用户模块介绍】也说明了注册接口的几个要求,比如:用户名不能为空、密码不能为空、密码不能小于8位,用户名不能重复等;
一:实现注册接口;
1.在UserController中,创建注册方法:register()方法;
/**
* 注册方法
* @param userName
* @param password
* @return
* @throws ImoocMallException
*/
@PostMapping("/register")
@ResponseBody
public ApiRestResponse register(@RequestParam("userName") String userName, @RequestParam("password") String password) throws ImoocMallException {
if (StringUtils.isEmpty(userName)) {//如果用户名为空,直接返回用户名不能为空的信息;
return ApiRestResponse.error(ImoocMallExceptionEnum.NEED_USER_NAME);
}
if (StringUtils.isEmpty(password)) {//如果密码为空,直接返回密码不能为空的信息;
return ApiRestResponse.error(ImoocMallExceptionEnum.NEED_PASSWORD);
}
if (password.length() < 8) {//如果密码长度小于8位,直接返回密码长度不能小于8位的信息;
return ApiRestResponse.error(ImoocMallExceptionEnum.PASSWORD_TOO_SHORT);
}
userService.register(userName, password);
return ApiRestResponse.success();
}
说明:
(1) 【@RequestParam】注解,有助于我们更好的识别和获取请求中的参数;如有需要,可以参考
●【“建议使用的方式”:使用【 RequestParam】】通过这篇博客可以看到,【@RequestParam】注解能够帮助获取get请求中的参数;
●【使用 RequestParam为参数设置默认值】通过这篇博客可以看到,【@RequestParam】不仅也可以帮助获取post请求中的参数,还能够设置默认值;

(2) 设置请求方式和url;添加上@ResponseBody注解;

(3) 判断【用户名不能为空】、【密码不能为空】、【密码不能小于8位】;

PS:自己以前做过的几个项目,这类【用户名不能为空】、【密码不能为空】、【密码不能小于8位】工作是放在前端来做的;但是,这儿我们把这个工作放在了后端来做;(这个情况,需要知道)
(4) 这儿判空时,我们没有使用【userName == null】的方式(因为,有的时候,前端确实传过来了userName,这个userName字符串是个空字符串;那么此时,这种方式就可能出现误判);而是使用的Springframework提供的一个工具【StringUtils.isEmpty(userName)】来判空;

(5) 如果上面的【用户名不能为空】、【密码不能为空】、【密码不能小于8位】这三个检查都通过了,那么就可以正式开始调用Service层的方法,去注册了;service层的逻辑,可以参考下面的【2.在UserService接口中,定义注册的方法:register()方法;】和【3.在UserServiceImpl实现类中,去实现register()注册方法;】
(6) 自然,因为Service层的register()方法存在抛出异常的情况;所以,Controller层在调用Service层的这个方法时候,Controller层也需要处理这个异常;这儿我们是把这个异常,继续向上抛;(关于,这个的最终处理,在 【Spring Boot电商项目用户模块注册接口开发之全局统一处理异常】中做了介绍)

(7) 自然,如果程序一切正常,service层也没有抛异常,即【userService.register(userName, password);】也没有异常,那么就代表注册成功了;那么,此时Controller层,直接返回给前端一个代表成功的ApiRestResponse就行了;

2.在UserService接口中,定义注册的方法:register()方法;

注意:下面实现类抛出了异常,接口也要设置抛出相同异常!!!
3.在UserServiceImpl实现类中,去实现register()注册方法;
/**
* 注册方法
* @param userName
* @param password
*/
@Override
public void register(String userName, String password) throws ImoocMallException {
//首先,查询用户名是否存在;注册时,不允许重名;
User result = userMapper.selectByName(userName);
if (result != null) {
//如果result不为null,这说明已经有这个用户了;此时,就可以直接向上抛异常;
throw new ImoocMallException(ImoocMallExceptionEnum.NAME_EXISTED);
}
//如果上面没有抛异常,那么说明用户名没有重名,那么我们就可以把这个注册信息写到数据库中
User user = new User();
user.setUsername(userName);
user.setPassword(password);
int count = userMapper.insertSelective(user);
if (count == 0) {
throw new ImoocMallException(ImoocMallExceptionEnum.INSERT_FAILED);
}
}
说明:
(1) 其中的【userMapper.selectByName(userName)】根据userName查询用户的方法,是我们自己定义的;参见下一部分:【4.在UserMapper接口中,定义根据userName查询用户的方法,并在UserMapper.xml中编写对应的SQL;】
(2)一个特别重要的点: 在Service这儿,如果判定【UserName重名】了,那么我们应该给出错误提示;
如果这是在Controller层,那么我们可以直接像前面那样,直接返回一个【ApiRestResponse.error(ImoocMallExceptionEnum.***)】这之类的错误信息:

但是,我们这是在Service层,由于每一层的分工是明确的;Service层只负责业务逻辑的处理,Service层不应该去触碰那些【和最终返回,相关的内容】,因为这是Controller的职责;
所以,这儿我们采用【向上抛出异常】的策略,来解决这个问题;所以,这就涉及到了自定义异常,在【SpringBoot电商项目用户模块注册接口开发之自定义异常类】中对这个问题进行了详细介绍;
(3) 如果判定这儿用户重复了,那么我们就向上抛出对应的异常:(自然,要在枚举类中,定义该错误对应的枚举信息)

(4) 如果判定这儿用户没有重复,那么我们就可以调用Dao层的【userMapper.insertSelective(user)】方法,来把注册信息写到数据库中;
(5) 【userMapper.insertSelective(user)】说明:
● 自然【userMapper.insertSelective(user)】是 我们在【项目初始化创建SpringBoot项目(必看)】中,通过【mybatis-generator】插件生成的;
● 【insertSelective()】方法内容分析:核心就是,insertSelective()允许插入一个【部分属性有值的,User对象】;

● 另一个比较类似的【insert()】方法,则要求:【全部属性都有值的,User对象】;

● 通过上面的分析,就会明白,为什么注册的时候,我们使用【insertSelective()】方法,而没有使用【insert()】方法了;(因为注册的时候,User对象的属性,只有username和password是有值的)
(6) 然后,如果Dao层操作数据库失败了,对于这个错误,也需要向上抛出异常;(自然,要在枚举类中,定义该错误对应的枚举信息)

4.在UserMapper接口中,定义根据userName查询用户的方法,并在UserMapper.xml中编写对应的SQL;
声明:
因为这儿我们在注册时,有一个要求是【用户名不能重复】;所以,我们就需要根据传过来的userName去数据库中查,看是否重名;
但是,我们在【项目初始化创建SpringBoot项目(必看)】中,通过【mybatis-generator】插件生成实体类、mapper接口、mapper.xml等逆向文件中,没有【根据userName查询用户】;所以,这儿需要我们自己去定义;
(1)在UserMapper接口中,定义根据userName查询用户的方法;

(2) 在UserMapper.xml中编写对应的SQL;
<select id="selectByName" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from imooc_mall_user
where username=#{userName,jdbcType=VARCHAR}
</select>说明:
(1) 方法名要对应上,接收的参数类型是String类型,返回的数据类型是我们在上面定义的一个resultMap;


(2) mybatis中的一个用法:【include refid=“Base_Column_List”/】,即使用include来引用SQL片段;

(3) 在使用#{}接收参数时,也可以指定数据类型哦;

(4) 因为这儿只有一个参数:所以,可以使用#{userName},也可以使用#{value};

二:测试接口;
1.前置的一点说明;
启动项目;使用postman去测试接口;在以前【附加:一般情况下,通过浏览器的地址栏输入url,发送的只能是get请求;(即【哪些情况下会发送get请求】,【哪些情况下会发送post请求】)】我们就说过,发起post请求的两种方式:【设置表单method = “post”】和【使用postman模拟】;而,通过浏览器直接发起的只能是get请求;

2.使用postman来测试;
(0)在postman创建文件夹和测试用的请求;


(1)测试1:正常的情况;

说明,一个接口能执行正常的情况,这是最基本的;我们更多的需要考虑异常的情况,即【接口能应对异常的情况】更重要;
(2)测试2:用户名为空的情况;

(3)测试3:密码为空的情况;

(4)测试4:密码小于8位;

(5)测试5:用户名重复;

原因分析:用户名重复,是Service层处理的这个错误,Service层对此向上抛出了一个异常;然后,Controller没有处理这个异常,其继续向上抛了;

所以,出于两个目的:
目的1:【抛出异常时,Controller也能按照ApiRestResponse统一返回对象的格式(其实也就是接口文档中定义的返回格式),向前端返回数据】;(这个目的就是,返回格式的统一啦)
目的2:【抛出异常时,隐藏异常的隐私信息】;(这个目的就是,安全啦)
当抛出异常时,我们应该对其进行处理,以达到上面的两个目的;为此,在【 待写…… 】中,我们进行了说明;