面试宝典三 --学科管理模块(拦截器,token,统一异常处理)
面试宝典
- 需求分析
- 涉及到的表,实体类,页面
- 学科管理页面跳转
- 新增学科
- 分析
- 前端统一设置拦截器
- 后台登录拦截配置
- 统一异常处理
- RequestException:
- 前端新增学科
- 后端逻辑
- CourseController
- CourseService
- CourseMapper
- 学科列表
- 请求处理封装
- QueryPageBean类
- 分页查询处理
- http/index.js:
- CourserController
- CourseService
- CourseMapper
- 更新学科
- 前端
- CourseController
- 删除学科
- 前端
- CourseController
- CourseService
需求分析
- 学科管理模块,需要完成学科的列表展示、新增、更新、删除四个功能;
- 学科列表展示
学科列表需要展示学科创建者,故创建的每个学科,需要关联当前用户ID;
学科列表需要展示管理的题目数量、标签数量、二级目录数量,这些查询需要嵌入子查询,
开始可以先写固定数值,等调试成功后,再细化数值。
列表展示需要分页显示,每页显示10条记录; - 新增、更新学科后刷新当前列表。
- 删除学科,如果学科下已有数据,不能删除该学科;
涉及到的表,实体类,页面
- 表
学科业务相关的表有t_course(学科表)、t_catalog(学科目录表,即对应的阶段)、t_tag(标签表,即
对应的知识点),学科表与学科目录、学科标签表都是1对多的关系。 - 实体类
- 页面 courseList.html
学科管理页面跳转
新增学科
分析
后台:后台提供新增学科的接口,接收学科数据,但是在设计添加学科的时候还必须知道关联的登录用户,而我们的用户信息是通过Token进行绑定的,因此需要由前台传递过来进行接收。并且,既然是后台数据应该务必要保证用户登录有效状态下才可以进行接口数据访问,因此我们可以在后台统一定制拦截器进行控制判断。
前端:在新增学科弹窗,点击确定后想后台发送学科数据。另外,后端需要接收Token认证信息,因此前端的请求必须发送Token信息,后期其他请求都是面向后台接口的,可以直接全局设置一个axios拦截器配置。
前端统一设置拦截器
// 添加axios实例请求拦截器,在请求头中添加用户登录的token信息
instance.interceptors.request.use(config => {var username = sessionStorage.getItem("userName");if (sessionStorage.getItem(username)) {config.headers[username] = sessionStorage.getItem(username);config.headers["tokenname"]=username;}return config;},error => {return Promise.reject(error);}
);
后台登录拦截配置
首先,在后台的com.cs.config包下创建一个自定义登录拦截器LoingIntercepter。在preHandle方法中
获取用户登录携带的Token信息,只有验证通过才可以访问后台。
package com.blb.msbd.intercepter;import com.blb.msbd.exception.MsException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 定制登录拦截器,查看登录的token信息*/
@Component
public class LoingIntercepter implements HandlerInterceptor {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponseresponse, Object handler) throws Exception {// 先判断请求路径// String url = request.getRequestURL().toString();
// if (url.contains("/api/user/login")) {// // 表示登录请求,可以直接放行处理
// return true;
// }// 其他情况,必须要用户登录情况下进行访问// 获取请求头信息String tokenname = request.getHeader("tokenname");String token = request.getHeader(tokenname);// 判断是否有token信息if (token == null) {// 没有token,throw new MsException(415, "没有携带Token信息,无访问权限!");} else {// 有携带token的情况下,需要跟服务器的token进行身份验证String redis_token = stringRedisTemplate.opsForValue().get(tokenname);if (redis_token != null && redis_token.equals(token)) {return true;} else {throw new MsException(415, "Token信息不正确,无访问权限!");}}}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponseresponse, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponseresponse, Object handler, Exception ex) throws Exception {}
}
接着,在自定义Webmvc配置类MyWebMvcConfig将上面自定义的LoingIntercepter拦截器类进行注册
使用方可生效
MyWebMvcConfig:
这里放行了登录和后面微信的一些接口,和swagger测试的接口
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {@Autowiredprivate LoingIntercepter loingIntercepter;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loingIntercepter).addPathPatterns("/**").excludePathPatterns("/api/user/login").excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**").excludePathPatterns("/common/**").excludePathPatterns("/wxMember/**");}
}
统一异常处理
在上一个新增学科的案例中,根据项目需要我们在后台统一定义了一个拦截器进行Token认证拦截,如
果失败是直接return false。这种情况下,前台vue项目是会直接进入exception中的,也就没有什么异
常提示说明,不是很友好。
因此,我们可以对拦截器的返回进行改造,自定义异常返回结果,同时前台可以接受异常信息进行页面
提示。
首先创建一个com.cs.web.exception包,在改包下可以进行统一异常处理类设置。为了区分不同,自定义一个异常类MsException。
package com.blb.msbd.exception;import lombok.Data;/*** 自定义异常类*/
@Data
public class MsException extends RuntimeException {// 异常状态码private Integer status;public MsException(Integer status) {this.status = status;}public MsException(String message) {super(message);}public MsException(Integer status, String message) {super(message);this.status = status;}
}
接着,自定义一个统一的全局的异常结果处理类RequestException。
RequestException:
package com.blb.msbd.exception;import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;/*** 对所有异常进行统一处理*/
@ControllerAdvice
public class RequestException {/*** 通用运行期异常处理*/
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<String> handleRuntimeException(RuntimeException e){return ResponseEntity.status(500).body(e.getMessage());
}/*** 针对我们自定义的异常类进行处理* @param e* @return*/@ExceptionHandler(MsException.class)public ResponseEntity exceptionResult(MsException e){// 返回http封装的结果对象return ResponseEntity.status(e.getStatus()).body(e.getMessage());}
}
前端新增学科
//新增学科确定handleCreateConfirm() {this.$refs['form'].validate((valid) => {if (valid) {let params = this.form;console.log("学科添加请求参数:");console.log(params);
// 发送请求addCourse(params).then(resp =>{if (resp.data.flag) {//1.添加成功this.$message({type:"success",showClose:true,message: resp.data.message})
//2. 让弹窗消失this.dialogFormVisible = false
//3. 重新查询所有学科信息this.getList();}else {//添加失败this.$message({type:"error",showClose:true,message: resp.data.message})}}).catch(error =>{console.log("添加学科异常信息:"+error.response.data);
//添加失败this.$message({type:"error",showClose:true,message: error.response.data})})}});},
后端逻辑
CourseController
@ApiOperation("添加学科")@PostMapping("/add")public Result add(@RequestBody Course course, HttpServletRequest request){try {//补充course缺少的数据//设置createDatecourse.setCreateDate(DateUtils.parseDate2String(new Date()));//设置userIdString tokenname = request.getHeader("tokenname");String token = stringRedisTemplate.opsForValue().get(tokenname);if(token!=null){String userid = token.substring(0, token.indexOf("_"));course.setUserId(Integer.parseInt(userid));}course.setOrderNo(1);boolean flag= courseService.addCourse(course);if(flag){return new Result(true,"添加课程成功");}else{return new Result(false,"添加课程失败");}} catch (Exception e){return new Result(false,e.getMessage());}}
CourseService
@Overridepublic boolean addCourse(Course course) {int insert = courseMapper.insert(course);return insert > 0;}
CourseMapper
@Repository
public interface CourseMapper extends BaseMapper<Course> {}
学科列表
请求处理封装
请求参数格式, 我们后台使用QueryPageBean接收
客户端请求参数格式
{currentPage: 1,pageSize: 10,queryParams:{name: '',status: '}
}
QueryPageBean类
package com.blb.msbd.entity;import lombok.Data;import java.io.Serializable;
import java.util.Map;/*** @description : 封装查询条件* @version: 1.0*/
@Data
public class QueryPageBean implements Serializable{private Integer currentPage; // 页码private Integer pageSize; //每页记录数private Map queryParams; //查询条件private Integer offset; // 分页查询,开始记录下标/*** 获取分页起始记录位置* 根据分页页数,计算limit其实记录* @return*/public Integer getOffset(){return (currentPage-1)*pageSize;}
}
分页查询处理
因为后台使用的是基于MyBatis-plus的springboot项目,所以需要使用ybatisPlus分页插件,就得需要
配置才可生效。
在com.cs.config包下添加ybatisPlus分页插件。
package com.blb.msbd.config;import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MybatisPlusConfig {/*** 分页插件*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return mybatisPlusInterceptor;}
}
http/index.js:
// 获取所有学科列表
export const getCourses = params => Instance.get("/api/course/findAll",params);
// 新增学科
export const addCourse = params => Instance.post2("/api/course/add",params);
//学科管理分页展示学科数据
export const getCourseByPage= params => Instance.post2("/api/course/findByPage",params);
//修改学科
export const updateCourse = params => Instance.post2("/api/course/update",params);
//删除学科
export const deleteCourse = params => Instance.post("/api/course/delete",params);
//查询所有学科
export const getAllCourse= params => Instance.post("/api/course/findAllCourse",params);
//查询所有学科,标签分类
export const findAllCourseCatTag= params => Instance.post("/api/course//findAllCourseCatTag",params);
// 学科分页列表getList() {// 必传参数let params = {currentPage: this.pagination.pageNum,pageSize: this.pagination.pageSize,queryParams:this.requestParameters};console.log("学科分页列表请求参数:");console.log(params);getCourseByPage(params).then(response=>{//查询成功if(response.data.flag){//将查询到的总数据条数total、当前页的数据集合rows赋值给vue的数据模型(pagination.total、items)this.pagination.total = response.data.result.total;this.items = response.data.result.records;this.$message.success("获取学科列表成功")}else{//查询失败this.$message({type:"error",message:response.data.message,showClose:true})}}).catch(error=>{this.$message.error(error.response.data)})},
CourserController
@ApiOperation("分页查询")@PostMapping("/findByPage")public Result findByPage(@RequestBody(required = false)QueryPageBean queryPageBean){//创建page对象,设置分页情况Page<Course> page = new Page<>(queryPageBean.getCurrentPage(), queryPageBean.getPageSize());Page<Course> coursePage=courseService.findByPage(page,queryPageBean);return new Result(true,"查询学科列表成功",coursePage);
CourseService
@Overridepublic Page<Course> findByPage(Page<Course> page, QueryPageBean queryPageBean) {QueryWrapper<Course> courseQueryWrapper = new QueryWrapper<>();Map queryParams = queryPageBean.getQueryParams();if(queryParams !=null){if(queryParams.get("name")!=null){courseQueryWrapper.like("name",queryParams.get("name"));}if(queryParams.get("status")!=null){courseQueryWrapper.eq("isShow",queryParams.get("status"));}}Page<Course> coursePage = courseMapper.selectPage(page, courseQueryWrapper);List<Course> courses = coursePage.getRecords();for (Course course : courses) {// 关联查询对应的创建用户User user = userMapper.selectById(course.getUserId());course.setCreator(user.getUsername());//查询二级目录QueryWrapper<Catalog> catalogQueryWrapper = new QueryWrapper<>();catalogQueryWrapper.eq("courseId", course.getId());List<Catalog> catalogs = catalogMapper.selectList(catalogQueryWrapper);Long Catalogcount = catalogMapper.selectCount(catalogQueryWrapper);course.setCatalogList(catalogs);course.setCatalogQty(Catalogcount.intValue());//查询标签的数量QueryWrapper<Tag> tagQueryWrapper = new QueryWrapper<>();tagQueryWrapper.eq("courseId", course.getId());Long Tagcount = tagMapper.selectCount(tagQueryWrapper);course.setTagQty(Tagcount.intValue());// 题目数量QueryWrapper<Question> questionQueryWrapper = new QueryWrapper<>();questionQueryWrapper.eq("courseId", course.getId());Long qcount = questionMapper.selectCount(questionQueryWrapper);course.setQuestionQty(qcount.intValue());}return coursePage;}
CourseMapper
@Repository
public interface CourseMapper extends BaseMapper<Course> {}
当然,也可以使用复杂的多表查询语句实现。如下:
<sql id="select_where"><where><if test="queryParams != null"><if test="queryParams.status != null and queryParams.status != ''">and isShow=#{queryParams.status}</if><if test="queryParams.name != null and queryParams.name != ''">and name like concat("%",#{queryParams.name},"%")</if></if></where></sql><select id="findTotalCount" resultType="long" parameterType="QueryPageBean">select count(*) from t_course<include refid="select_where"></include></select><select id="findPageList" resultType="com.itheima.mm.pojo.Course"parameterType="QueryPageBean">selectid,name,isShow,createDate,(select count(*) from t_catalog where courseId=c.id) catalogQty,(select count(*) from t_tag where courseId=c.id) tagQty,(select count(*) from t_question where courseId=c.id) questionQty,(select username from t_user where id=c.userId) creatorfrom t_course c<include refid="select_where"></include>limit #{offset},#{pageSize}</select>
查询当前页数据集合的SQL语句分析
– 查询当前学科的二级目录个数
select count() from t_catalog where courseId=1
– 查询当前学科的标签个数
select count() from t_tag where courseId=1
– 查询当前学科的题目个数
select count(*) from t_question where courseId=1
– 查询创建者
select username from t_user where id=1
更新学科
前端
// 修改学科确定handleUpdateConfirm() {this.$refs['form'].validate((valid) => {if (valid) {let params = this.form;console.log("学科更新请求参数:");console.log(params);updateCourse(params).then(response=>{if(response.data.flag){//修改成功this.$message({message:response.data.message,type:"success",showClose:true})//隐藏弹窗this.dialogFormVisible = false;//重新查询列表this.getList()}else {//修改失败this.$message({message:response.data.message,type:"error",showClose:true})}})
CourseController
@ApiOperation("修改课程")@PostMapping("/update")public Result updateCourse(@RequestBody Course course){boolean update = courseService.updateById(course);if(update){return new Result(true,"修改成功");}else{return new Result(true,"修改失败");}}
删除学科
点击删除学科,绑定点击事件,同时传递要删除的数据参数。在vue方法中应该先提示是否删除,如果
确定删除再发送axios请求。
前端
// 删除学科handleDeleted(row) {this.$confirm('此操作将永久删除学科 ' + ', 是否继续?', '提示', {type: 'warning'}).then(() => {let params={courseId:row.id};deleteCourse(params).then(response=>{if(response.data.flag){//删除成功this.$message({type:"info",message:"删除成功",showClose:true})this.getList();}else{//删除失败this.$message({type:"error",message:response.data.message,showClose:true})}})}).catch(() => {this.$message.info('已取消操作!')});}
CourseController
@PostMapping("/delete")public Result delete(Integer courseId){courseService.deleteById(courseId);try {return new Result(true,"删除成功");}catch (Exception e){return new Result(true,"删除失败");}}
CourseService
@Overridepublic void deleteById(Integer courseId) {//1. 判断当前要删除的学科是否有关联的二级目录
//根据courseId到t_catalog表中查询数据条数// 二级目录数量QueryWrapper<Catalog> catalogQueryWrapper = new QueryWrapper<>();catalogQueryWrapper.eq("courseId", courseId);Long catalogCount = catalogMapper.selectCount(catalogQueryWrapper);if (catalogCount > 0) {//有关联的二级目录,不能删除throw new RuntimeException("有关联的二级目录,不能删除");}
//2. 判断当前要删除的学科是否有关联的标签QueryWrapper<Tag> tagQueryWrapper = new QueryWrapper<>();tagQueryWrapper.eq("courseId",courseId);Long tagCount = tagMapper.selectCount(tagQueryWrapper);if (tagCount > 0) {//有关联的标签,不能删除throw new RuntimeException("有关联的标签,不能删除");}
//3. 判断当前要删除的学科是否有关联的题目QueryWrapper<Question> questionQueryWrapper = new QueryWrapper<>();questionQueryWrapper.eq("courseId",courseId);Long questionCount = questionMapper.selectCount(questionQueryWrapper);if (questionCount > 0) {//有关联的题目,不能删除throw new RuntimeException("有关联的题目,不能删除");}
//4. 如果上述三个关联都没有,则根据id删除courseMapper.deleteById(courseId);}
面试宝典三 --学科管理模块(拦截器,token,统一异常处理)相关推荐
- Javaweb入门到实战(三)过滤器、拦截器、jdbc详解
9.JavaBean 实体类 JavaBean有特定的写法: 必须要有一个无参构造 属性必须私有化 必须有对应的get/set方法: 一般用来和数据库的字段做映射 ORM ORM:对象关系映射 表–& ...
- axios的响应拦截器 - 错误统一处理 / 获取数据繁琐
1. 问题 登录时,填写错误的用户名密码,没有错误提示(没有进入catch分支) 获取ajax的返回结果比较麻烦:res.data.data.xxxx 2.解决登录失败不报错的问题 2.1 分析原因 ...
- vue拦截器实现统一token,并兼容IE9验证
项目中使用vue搭建前端页面,并通过axios请求后台api接口,完成数据交互.如果验证口令token写在在每次的接口中,也是个不小的体力活,而且也不灵活.这里分享使用vue自带拦截器,给每次请求的头 ...
- axios请求拦截器错误_React中使用高阶组件和axios的拦截器,统一处理请求失败提示...
在前端开发中,判断边界条件和重要,通常我们要花费开发中的很大一部分时间做边界条件处理.发送ajax请求时,假设有这样一个需求: 每个页面发送ajax请求,如果请求失败,在页面上统一弹出样式一样的错误提 ...
- springmvc高级(拦截器,全局异常处理,文件上传)
SpringMVC 1.文件上传 文件上传: 指的就是将用户本地计算机中文件上传到服务器上的过程称之为文件上传 1).文件上传编程步骤 # 1.项目中引入相关依赖 <dependency> ...
- 企业实战之Spring拦截器《统一参数校验》
在前面的一些文章中我们有讲到,通过拦截器我们可以做很多的事情,包括接口统一的 参数校验. 登录校验.权限校验等,也可以做一些HTTP响应体写入逻辑,比如我们另一篇文章所说的<解决跨域问题> ...
- think in java interview-高级开发人员面试宝典(三)
comparable接口与comparator 两种比较接口分析 前者应该比较固定,和一个具体类相绑定,而后者比较灵活,它可以被用于各个需要比较功能的类使用. 一个类实现了 Camparable 接口 ...
- 面试突击90:过滤器和拦截器有什么区别?
- SpringBoot+拦截器+自定义异常+自定义注解+全局异常处理简单实现接口权限管理...
点击关注公众号,实用技术文章及时了解 来源:blog.csdn.net/weixin_44102992/article/ details/107335702 前言 提到权限管理这块肯定很多人第一想到的 ...
最新文章
- 面向对象的设计原则最终篇
- python No tests were found问题解决方法
- 又在GitHub上挖到个宝藏:Switch模拟器!
- 热电偶校验仪使用说明_热电偶冷端补偿方法
- html5步骤条,自定义步骤条 , 纯原生html + css
- qt 关闭窗口的槽函数_勇哥的VC++应用框架学习之QT(1) 信号槽、按钮控件、opencv读取显示图片...
- Shell自动上传下载文件到SFTP服务器
- ios html异步加载图片,iOS 异步加载本地图片
- 【流媒體】live555—VS2010 下live555编译、使用及测试
- select模型使用例子
- perl多进程实战之一
- paper 134:结构张量structure tensor(二)
- Android开发--Button的应用
- Windows的SVN的下载和安装
- windows下ping端口
- javaFX学习之颜色选择器(ColorPicker)
- python3GUI——微博图片爬取工具
- linux cat 压缩文件,Linux cat和zcat命令可能比你意识到的更有用
- CSS3图片上下移动的动画效果
- XXE漏洞以及XXE漏洞如何修复