图书管理系统(微信、后台、网页端)总结
Web课程技术总结
网页前端
基本框架
vue
1、值存储问题
需要变更属性时(update),需要先获取属性(get),这里在返回时需要在服务端将值设为null,在更新时需要采用selective的方法,updateSelective()。放入session中的时候需要将password设为空,来保证update的时候不会被更新为空值。
2、值存储事例
get
@RequestMapping("/getBookAdmin")
public AjaxResult A12(HttpServletRequest request) {
BookAdmin bookAdmin = bookAdminService.getBookAdmin(request);
bookAdmin.setAdpassword(null);
return GENERATE_SUCCESS_RESULT(bookAdmin);
}
update
//SysAdminController.java
@RequestMapping("/getSysAdmin")
public AjaxResult A12(HttpServletRequest request) {
return GENERATE_SUCCESS_RESULT(userService.getSysAdmin(request));
}
//BookAdminService.java
public void update(BookAdmin bookAdmin) {
bookAdminMapper.updateByPrimaryKeySelective(bookAdmin);
}
3、前后端传值时,value和带分页的PageHelper的取值时候的区别
微信前端
1、值存储问题
具体问题与vue类似
2、data的使用
通过this对象也就是能在Page({})作用域中直接访问的值需要使用this.data.
的形式进行定位
3、全局变量的使用
微信中的全局变量定义在根目录下的app.js中,App({})
下包含的东西都是属于全局的
App({
global: {
}
}
)
使用方法通过在其他页面中用var app = getApp()
引入即可进行使用
全局变量可以用作存储页面跳转之间的临时变量也就是类似网页端两个jsp的传值通过
session而不是
request`来实现
4、wx小程序中的request
微信中的wx.request
是不带什么状态变量的,只通过发送无状态的http请求,这里的无状态是指他不携带除指定信息外的信息。比如session。
对于带有服务端权限校验,通过session的形式存储部分信息的java后台程序来说,就需要在微信端自行保存session值并通过每次发送请求携带Header来实现。通过将session值保存到之前介绍的全局变量中,每次发送请求带上相应的header即可。
//全局作用域中添加Header变量
App({
global: {
header:{
'Cookie':''
}
}
})
//在登陆或者初始化请求时进行获取session值
wx.request({
url: "http://localhost:8080/login",
method: 'post',
data: e.detail.value,
success: function(value) {
if (that.parseInfo(value)) {
//存储用户信息
app.global.user = value.data.data;
app.global.header.Cookie = value.cookies[0];
})
//在发送请求时携带Header
wx.request({
url: 'http://localhost:8080/user/updateUser',
method: 'post',
data: e.detail.value,
header: app.global.header,
success:function(res){
console.log(res);
}
});
前后端传值
1、使用JSON的形式进行前后端传值
前端传入值时采用application/json
的形式进行传值,并且保证了vue
和wx
的一致性
2、对返回值进行封装
使用AjaxResult方法对正确返回值进行封装,其中主要部分是使用AjaxResult携带正确或错误代码,如果出错或正确时返回相应的信息
@Data
public class AjaxResult<T> implements Serializable {
private T data = null;
private String url = null;
private String message = null;
private short resultCode;
}
后端
1、lombok的使用
2、PageHelper的完善
pageHelp的generateCheck代码如下
public static PageHelper generateCheckedHelper(int count, PageHelper pageHelper) {
if (pageHelper == null)
return pageHelper;
pageHelper.setCount(count);
if (pageHelper.getPageSize() == null || pageHelper.getPageSize() <= 1)
pageHelper.setPageSize(10);
pageHelper.setTotalPage(pageHelper.getTotalPage());
//判断当前页面是否为空
if (pageHelper.getCurrentPage() == null || pageHelper.getCurrentPage() <= 0)
pageHelper.setCurrentPage(1);
if (pageHelper.currentPage > pageHelper.totalPage)
pageHelper.setCurrentPage(pageHelper.totalPage);
if (pageHelper.getCurrentPage() == null || pageHelper.getCurrentPage() <= 0)
pageHelper.setCurrentPage(1);
return pageHelper;
}
pageHelper的流程图如下
flow
en=>end: 返回pageHelper
st=>start: 开始
setCount=>operation: setCount
pageHelperIsNull=>condition: pageHelper为空
pageSizeIsNullOrLow=>condition: pageSize==null||pageSize<=1
setPageSize=>operation: setPageSize(10)
setTotalPageSize=>operation: setTotalPage(totalPage);
curPageIsNullOrLow1=>condition: curPage==null||curPage<=0
setCurPage21=>operation: setCurrentPage(1);
curPageBigTotalPage=>condition:currentPage>totalPage
setCurPage2TotalPage=>operation:setCurrentPage(totalPage)
curPageIsNullOrLow2=>condition: curPage==null||curPage<=0
setCurPage22=>operation: setCurrentPage(1);
st->pageHelperIsNull
pageHelperIsNull(yes)->pageSizeIsNullOrLow
pageHelperIsNull(no)->setCount
setCount->pageSizeIsNullOrLow
pageSizeIsNullOrLow(yes)->setPageSize
setPageSize->setTotalPageSize
pageSizeIsNullOrLow(no)->setTotalPageSize
setTotalPageSize->curPageIsNullOrLow1
curPageIsNullOrLow1(yes)->setCurPage21
setCurPage21->curPageBigTotalPage
curPageIsNullOrLow1(no)->curPageBigTotalPage
curPageBigTotalPage(yes)->setCurPage2TotalPage
setCurPage2TotalPage->curPageIsNullOrLow2
curPageBigTotalPage(no)->curPageIsNullOrLow2
curPageIsNullOrLow2(yes)->setCurPage22
curPageIsNullOrLow2(no)->en
setCurPage22->en
3、DTO、BeanUtil、Objects、StringUtil的使用
BeanUtil
BeanUtil中常用的方法是copyProperties
,用于赋值属性值,但需要为相应的属性设置set和get方法
/*将source中所有属性复制到target中,实现时通过获取source中的每个属性get方法,并获取值。当某个属性没有get方法时会出错*/
public static void copyProperties(Object source, Object target) throws BeansException {
copyProperties(source, target, (Class)null, (String[])null);
}
public static void copyProperties(Object source, Object target, Class<?> editable) throws BeansException {
copyProperties(source, target, editable, (String[])null);
}
/*将source中所有属性复制到target中并忽略其中提到的ignoreProperties,实现时通过获取source中的每个属性get方法,并获取值。当某个属性没有get方法时会出错*/
public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException {
copyProperties(source, target, (Class)null, ignoreProperties);
}
Objects
package java.util;
public final class Objects {
//对于两个对象,这个也不错
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
//获取hash值
public static int hashCode(Object o) {
return o != null ? o.hashCode() : 0;
}
//对于数组
public static boolean deepEquals(Object a, Object b) {
if (a == b)
return true;
else if (a == null || b == null)
return false;
else
return Arrays.deepEquals0(a, b);
}
//必须为非空,这个挺好用的
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
}
4、权限拦截器和正则表达式
本次实验中一共有三个角色,需要对三种地址进行权限判断
- /sysAdmin/**
- /bookAdmin/**
- /user/**
@Configuration
public class MyConfig extends WebMvcConfigurerAdapter {
public void addInterceptors(InterceptorRegistry registry) {
//这里参数是一个实现了HandlerInterceptor接口的拦截器
registry.addInterceptor(reThis())
.addPathPatterns("/sysAdmin/**")//需要拦截的请求
.addPathPatterns("/bookAdmin/**")
.addPathPatterns("/user/**")
.excludePathPatterns("/*");
}
// @Bean
public MyInterceptor reThis() {
return new MyInterceptor();
}
}
在拦截器Interceptor中使用正则表达式进行判断,如果当前角色和访问的地址相符就通过,否则进行跳转并拦截,需要注意的是preHandler返回的值为boolean类型,如果为false代表当前拦截器处理全部信息不再向后传递信息。
@Configuration
public class MyInterceptor implements HandlerInterceptor {
//将bean注册到容器中
@Bean
public MyInterceptor retrunMy() {
return new MyInterceptor();
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean acc = false;
String url = request.getRequestURI();
Object user = request.getSession().getAttribute("user");
String patternForSysAdmin = "/sysAdmin/.*";
String patternForBookAdmin = "/bookAdmin/.*";
String patterForUser = "/user/.*";
if (user == null)
acc = false;
else if (user instanceof User) {
acc = Pattern.matches(patterForUser, url);
} else if (user instanceof SysAdmin) {
acc = Pattern.matches(patternForSysAdmin, url);
} else if (user instanceof BookAdmin)
acc = Pattern.matches(patternForBookAdmin, url);
System.out.println(String.format("###拦截器 请求的地址: %s , user值:%s , 不拦截?: %s", request.getRequestURI(), request.getSession().getAttribute("user"), acc));
if (!acc)
response.sendRedirect("/");
return acc;
}
}
需要注意的一点是.addPathPatterns和Pattern.matches中的正则表达式并不是一样的规范
5、异常处理和全局异常处理器
@ControllerAdvice
的方法进行拦截处理
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NullOrEmptyException.class)
@ResponseBody
public AjaxResult hanleNull(HttpServletRequest request, Exception exception) throws Exception {
System.out.println(String.format("接受到来自request: %s 的错误请求 , message: %s , localizedMessage : %s", request.getRequestURI(), exception.getMessage(), exception.getLocalizedMessage()));
exception.printStackTrace();
return ResultGenerator.GENERATE_FAILED_MESSAGE(exception.getMessage());
}
@ExceptionHandler(NullPointerException.class)
@ResponseBody
public AjaxResult hanleNullP(HttpServletRequest request, Exception exception) throws Exception {
System.out.println(String.format("接受到来自request: %s 的错误请求 , message: %s , localizedMessage : %s", request.getRequestURI(), exception.getMessage(), exception.getLocalizedMessage()));
exception.printStackTrace();
return ResultGenerator.GENERATE_FAILED_MESSAGE("参数不能为空");
}
@ExceptionHandler(SQLException.class)
@ResponseBody
public AjaxResult handlePK(HttpServletRequest request, Exception exception) throws Exception {
System.out.println(String.format("错误请求:MySQL异常,接受到来自request: %s 的错误请求 , message: %s , localizedMessage : %s", request.getRequestURI(), exception.getMessage(), exception.getLocalizedMessage()));
exception.printStackTrace();
return ResultGenerator.GENERATE_FAILED_MESSAGE("id不能重复");
}
}
存疑,拦截不到SQLException
6、view-controller的快捷使用
通过WebMvcConfigurerAdapter中的addViewControllers(ViewControllerRegistry registry)
方法添加viewController
,直接将 url
地址映射到/template/**
下面的模板页面中。而不是为每个template
都建立一个对用的mapping
@Configuration
public class MyConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//为模板/template/world.html设置一个名为/world的访问路径,因为设置了template的后缀为.html,当在现有的@RequestMapping中找不到/helloworld时就会跳到/template/world.html上
registry.addViewController("/world").setViewName("/helloworld");
}
}
7、session存储用户信息
通过session存储用户信息时,请将敏感信息类似于密码或者salt
这种信息设置为空而不是空字符串"",如果设置为空字符串可能导致部分用户信息在更新的时候丢失掉原来的信息。
((User) userDB).setPassword(null);
数据库
{}使用的是PraparedStatement
,直接通过设值的方式注入,属于DBMS进行检查,所以注入后的值会直接加上""
update TABLEA set a=${A};
${}使用的是字符串拼接方法,不能直接通过注入值的方式注入。在加入后,对于String A = "123"
加入后的值就是
update TABLEA set a=123;
但是#{}和${}可以混合使用
<when test="criterion.noValue">and ${criterion.condition}</when>