1. 测试controller层 10
1.1 添加Controller 10
service-oa模块
SysRoleController
package com.atguigu.auth.controller;
import com.atguigu.auth.service.SysRoleService;
import com.atguigu.model.system.SysRole;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//角色管理 控制层 10
@RestController //交给spring管理和返回json数据
@RequestMapping("/admin/system/sysRole")
public class SysRoleController {
//注入
@Autowired
private SysRoleService sysRoleService;
}
2. 查询所有角色 10
service-oa模块SysRoleController
//1 查询所有角色 和 当前用户所属角色 10
//测试的话访问这个路径 http://localhost:8800/admin/system/sysRole/findAll
@GetMapping("findAll")
public List<SysRole> findAll(){
//调用serivice的方法
List<SysRole> list = sysRoleService.list();
return list;
}
测试,启动启动类成功
浏览器输入http://localhost:8800/admin/system/sysRole/findAll
当然也可以使用postman测试
3. 定义统一返回结果对象 11
项目中我们会将响应封装成json返回,一般我们会将所有接口的数据格式统一, 使前端(iOS Android, Web)对数据的操作更一致、轻松。
一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。但是一般会包含状态码、返回消息、数据这几部分内容
例如,我们的系统要求返回的基本数据格式如下:
列表:
{
"code": 200,
"message": "成功",
"data": [
{
"id": 2,
"roleName": "系统管理员"
}
],
"ok": true
}
分页:
{
"code": 200,
"message": "成功",
"data": {
"records": [
{
"id": 2,
"roleName": "系统管理员"
},
{
"id": 3,
"name": "普通管理员"
}
],
"total": 10,
"size": 3,
"current": 1,
"orders": [],
"hitCount": false,
"searchCount": true,
"pages": 2
},
"ok": true
}
没有返回数据:
{
"code": 200,
"message": "成功",
"data": null,
"ok": true
}
失败:
{
"code": 201,
"message": "失败",
"data": null,
"ok": false
}
3.1 定义统一返回结果对象 11
操作模块:common-util
后续其他模块也会用到,故抽取到common-util模块
枚举ResultCodeEnum
package com.atguigu.common.result;
import lombok.Getter;
//这是一个枚举配置定义统一返回结果对象使用的 11
@Getter
public enum ResultCodeEnum {
SUCCESS(200,"成功"),
FAIL(201, "失败"),
;
private Integer code;
private String message;
private ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
统一结果返回类Result
package com.atguigu.common.result;
import lombok.Data;
//定义统一结果返回类 11
@Data
public class Result<T> {
private Integer code;//状态码
private String message;//返回信息
private T data;//数据
//私有化 ,私有化构造代表这个类不能new了,别人要想操作这个类的话只能使用此类中的static方法
private Result() {}
//封装返回是数据 11
public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
Result<T> result = new Result<>();
//封装数据
if(body != null) {
result.setData(body);
}
//状态码
result.setCode(resultCodeEnum.getCode());
//返回信息
result.setMessage(resultCodeEnum.getMessage());
return result;
}
//成功 无数据的 11
public static<T> Result<T> ok() {
return build(null,ResultCodeEnum.SUCCESS);
}
//成功 有数据的 11
public static<T> Result<T> ok(T data) {
return build(data,ResultCodeEnum.SUCCESS);
}
//失败 无数据的 11
public static<T> Result<T> fail() {
return build(null,ResultCodeEnum.FAIL);
}
//失败 有数据的 11
public static<T> Result<T> fail(T data) {
return build(data,ResultCodeEnum.FAIL);
}
public Result<T> message(String msg){
this.setMessage(msg);
return this;
}
public Result<T> code(Integer code){
this.setCode(code);
return this;
}
}
3.2 改造查询所有角色 11
service-oa模块
//改造 查询所有角色 和 当前用户所属角色 11
//测试的话访问这个路径 http://localhost:8800/admin/system/sysRole/findAll
@GetMapping("findAll")
public Result findAll(){
//调用serivice的方法
List<SysRole> list = sysRoleService.list();
return Result.ok(list);
}
浏览器测试
postman测试
4. knife4j 12
文档地址:https://doc.xiaominfo.com/
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。
4.1 Swagger介绍
前后端分离开发模式中,api文档是最好的沟通方式。
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
1、及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)
2、规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)
3、一致性 (接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)
4、可测性 (直接在接口文档上进行测试,以方便理解业务)
4.2 集成knife4j
knife4j属于service模块公共资源,因此我们集成到service-uitl模块
4.2.1 添加依赖
操作模块:service-uitl
pom.xml
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
说明:guigu-auth-parent已加入版本管理
4.2.2 添加knife4j配置类 12
操作模块:service-uitl
Knife4jConfig
package com.atguigu.common.config.knife4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
import java.util.ArrayList;
import java.util.List;
//Knife4j配置类,knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。
//便于我们测试,前后端统一接口等作用 12
public class Knife4jConfig {
@Bean
public Docket adminApiConfig(){
List<Parameter> pars = new ArrayList<>();
ParameterBuilder tokenPar = new ParameterBuilder();
tokenPar.name("token")
.description("用户token")
.defaultValue("")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.build();
pars.add(tokenPar.build());
//添加head参数end
Docket adminApi = new Docket(DocumentationType.SWAGGER_2)
.groupName("adminApi")
.apiInfo(adminApiInfo())
.select()
//只显示admin路径下的页面
.apis(RequestHandlerSelectors.basePackage("com.atguigu"))
.paths(PathSelectors.regex("/admin/.*"))
.build()
.globalOperationParameters(pars);
return adminApi;
}
private ApiInfo adminApiInfo(){
return new ApiInfoBuilder()
.title("后台管理系统-API文档")
.description("本文档描述了后台管理系统微服务接口定义")
.version("1.0")
.contact(new Contact("atguigu", "http://atguigu.com", "atguigu@qq.com"))
.build();
}
}
4.2.3 Controller层添加注解 12
service-oa 模块
SysRoleController
测试启动服务
浏览器输入http://localhost:8800/doc.html 成功
5. 条件分页查询角色 13
第一步配置分页插件
第二步编写controller的分页方法
(1)需要参数:分页相关参数(当前页和每页显示记录数)
(2)需要参数:条件参数
第三步调用service的方法实现条件分页查询
5.1 启动类修改 13
service-oa模块启动类ServiceAuthApplication
package com.atguigu.auth;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
//启动类 6
@SpringBootApplication
@ComponentScan("com.atguigu") //设置扫描规则,要扫描那些包这里是扫描com.atguigu包及其子包
public class ServiceAuthApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceAuthApplication.class, args);
}
}
5.1 配置分页插件 13
操作模块:service-uitl,service公共资源说明:我们将@MapperScan("com.atguigu.auth.mapper")提取到该配置类上面,统一管理,启动不需要了。
MybatisPlusConfig
package com.atguigu.common.config.mp;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//这是一个分页插件 13
@Configuration
@MapperScan("com.atguigu.auth.mapper")//表示能找到mapper动态创建的对象
public class MybatisPlusConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则, 13
* 需要设置 MybatisConfiguration#useDeprecatedExecutor = false
* 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
}
5.2 条件分页查询实现 13
service-oa模块
SysRoleController
//条件分页查询 13
//page 当前页 limit 每页显示记录数
//SysRoleQueryVo 条件对象
@ApiOperation("条件分页查询")
@GetMapping("{page}/{limit}")
public Result pageQueryRole(@PathVariable Long page,
@PathVariable Long limit,
SysRoleQueryVo sysRoleQueryVo) {
//调用service的方法实现
//1 创建Page对象,传递分页相关参数
//page 当前页 limit 每页显示记录数
Page<SysRole> pageParam = new Page<>(page,limit);
//2 封装条件,判断条件是否为空,不为空进行封装
LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();
String roleName = sysRoleQueryVo.getRoleName();
if(!StringUtils.isEmpty(roleName)) {
//封装 like模糊查询
wrapper.like(SysRole::getRoleName,roleName);
}
//3 调用方法实现
IPage<SysRole> pageModel = sysRoleService.page(pageParam, wrapper);
return Result.ok(pageModel);
}
测试http://localhost:8800/doc.html
添加条件
6. 添加角色 14
service-oa模块
SysRoleController
//添加角色 14
@ApiOperation("添加角色")
@PostMapping("save")
public Result save(@RequestBody SysRole role) {//@RequestBody是通过json格式传递封装进对象中
//调用service的方法
boolean is_success = sysRoleService.save(role);
if(is_success) {
return Result.ok();
} else {
return Result.fail();
}
}
7. 修改角色 14
service-oa模块
SysRoleController
//修改角色-根据id查询 14
//这个方法说白了就是根据id查询
@ApiOperation("根据id查询")
@GetMapping("get/{id}")//使用了restful风格
public Result get(@PathVariable Long id) {//@PathVariable得到restful传来的值
SysRole sysRole = sysRoleService.getById(id);
return Result.ok(sysRole);
}
//修改角色-最终修改 14
@ApiOperation("修改角色")
@PutMapping ("update")
public Result update(@RequestBody SysRole role) {
//调用service的方法
boolean is_success = sysRoleService.updateById(role);
if(is_success) {
return Result.ok();
} else {
return Result.fail();
}
}
8. 删除角色(id删除和批量删除) 14
service-oa模块
SysRoleController
//根据id删除 14
@ApiOperation("根据id删除")
@DeleteMapping("remove/{id}")
public Result remove(@PathVariable Long id) {
boolean is_success = sysRoleService.removeById(id);
if(is_success) {
return Result.ok();
} else {
return Result.fail();
}
}
这里做个解释
json格式有两种 @RequestBody会根据你传的参数来进行不同的封装
//批量删除 14
// 前端数组 [1,2,3]
@ApiOperation("批量删除")
@DeleteMapping("batchRemove")
public Result batchRemove(@RequestBody List<Long> idList) {
boolean is_success = sysRoleService.removeByIds(idList);
if(is_success) {
return Result.ok();
} else {
return Result.fail();
}
}
9. 配置日期时间格式 15
service-oa模块
application-dev.yml
jackson: # 配置时间 15
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
再次查询时间格式正确
10. 统一异常处理 15
10.1 制造异常
service-oa模块
我们给findAll查询所有模拟异常
SysRoleController
//改造 查询所有角色 和 当前用户所属角色 11
//测试的话访问这个路径 http://localhost:8800/admin/system/sysRole/findAll
@ApiOperation("查询所有的角色") //knife4j生成接口文档的中文说明 12
@GetMapping("findAll")
public Result findAll(){
//调用serivice的方法
List<SysRole> list = sysRoleService.list();
//模拟异常 15
int i = 10/0;
return Result.ok(list);
}
测试
10.2 统一异常 15
我们想让异常结果也显示为统一的返回结果对象,并且统一处理系统的异常信息,那么需要统一异常处理。
全局异常处理 15
service-util模块
GlobalExceptionHandler
package com.atguigu.common.config.exception;
import com.atguigu.common.result.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
//统一全局异常类 15
@ControllerAdvice //通过AOP将思想将此功能加到当前的类中去
public class GlobalExceptionHandler {
//全局异常处理,执行的方法
@ExceptionHandler(Exception.class)//指定哪个异常出现时会执行此方法
@ResponseBody//返回json数据 15
public Result error(Exception e) {
e.printStackTrace();
return Result.fail().message("执行全局异常处理...");
}
}
特定异常处理 16
service-util模块
GlobalExceptionHandler
//特定异常处理 16
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public Result error(ArithmeticException e) {
e.printStackTrace();
return Result.fail().message("执行特定异常处理...");
}
自定义异常处理 16
第一步创建异常类,继承RuntimeException
第二步在异常类添加属性,状态码和描述信息
第三步在出现异常地方,手动抛出异常
第四步在之前创建类,添加执行的方法
service-util模块
GuiguException
package com.atguigu.common.config.exception;
import com.atguigu.common.result.ResultCodeEnum;
import lombok.Data;
//自定义异常类 16
@Data
public class GuiguException extends RuntimeException {
private Integer code;//状态码
private String msg;//描述信息
public GuiguException(Integer code,String msg) {
super(msg);
this.code = code;
this.msg = msg;
}
/**
* 接收枚举类型对象
* @param resultCodeEnum
*/
public GuiguException(ResultCodeEnum resultCodeEnum) {
super(resultCodeEnum.getMessage());
this.code = resultCodeEnum.getCode();
this.msg = resultCodeEnum.getMessage();
}
@Override
public String toString() {
return "GuliException{" +
"code=" + code +
", message=" + this.getMessage() +
'}';
}
}
手动抛出异常(我们在查询所有中模拟)
service-oa模块
SysRoleController
//改造 查询所有角色 和 当前用户所属角色 11
//测试的话访问这个路径 http://localhost:8800/admin/system/sysRole/findAll
@ApiOperation("查询所有的角色") //knife4j生成接口文档的中文说明 12
@GetMapping("findAll")
public Result findAll(){
//调用serivice的方法
List<SysRole> list = sysRoleService.list();
//模拟异常 15
//int i = 10/0;
//模拟异常 手动抛出异常 验证我们的自定义异常 16
try {
int i = 10/0;
}catch (Exception e){
//抛出自定义异常
throw new GuiguException(20001,"执行了自定义异常处理..");
}
return Result.ok(list);
}
service-util模块
在全局异常类添加方法
GlobalExceptionHandler
//自定义异常处理 16
@ExceptionHandler(GuiguException.class)
@ResponseBody
public Result error(GuiguException e) {
e.printStackTrace();
return Result.fail().code(e.getCode()).message(e.getMsg());
}
测试,自定义异常成功