一,添加页面
问题一:
如果直接跳转到页面上,无法取得在session域内的值。
解决方法:需要跳转到后台页面用方法获取后跳转到指定的添加页面
@RequestMapping("/add.do") public String queryDepartmentAll(HttpSession session, Model model) { Object employees = session.getAttribute("employees"); if (employees != null) { System.out.println("部门数量:" + ((List<Employee>) employees).size()); } else { System.err.println("session中的数据丢失"); departmentList = departmentDao.getDepartments(); model.addAttribute("departments", departmentList); } return "add"; } |
问题二:
spring boot 表单的实体提交错误:Validation failed for object='book'. Error count: 2
解决方案:
在方法的实体类前面添加@Valid ,在后面添加BindingResult bindingResult
问题三:添加页面时,日期转换问题
解决方法:在下面类中添加Converter类
@Bean public Converter<String, Date> addNewConvert() { return new Converter<String, Date>() { @Override public Date convert(String source) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date date = null; try { date = sdf.parse(source); } catch (Exception e) { e.printStackTrace(); } return date; } }; }
问题四:添加与修改页面的提交方法不一致
处理办法:1.
问题五:springboot 表单重复提交
处理办法:
拦截器
Spring 拦截器有两种实现方法。一种是继承HandlerInterceptorAdapter,拥有preHandle(业务处理器处理请求之前被调用),postHandle(在业务处理器处理请求执行完成后,生成视图之前执行),afterCompletion(在完全处理完请求后被调用,可用于清理资源等)三个方法。
另一种就是调用 Spring AOP 的方法来实现。而且,我觉得这种方法更加灵活方便,所以我比较经常使用这种方法。
AOP( AspectJ— 注解 风格)
AOP 就是 Aspect Oriented Programming(面向方面编程)。
1. 连接点(Joinpoint):表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化、方法执行、方法调用、字段调用或处理异常等等,Spring只支持方法执行连接点。
2. 前置通知(@Before):在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。
3. 抛出异常后通知(@AfterThrowing):方法抛出异常退出时执行的通知
解决问题
表单重复提交
服务器认为是同一个表单,在短时间内重复(不止一次)提交,或者提交异常。比如,在服务器还没有响应前我们不断点击刷新网页上一个提交按钮,或者通过 ajax 不断对服务器发送请求报文!
防止情况
不通过正常路径访问页面表单; session 失效情况下提交表单; 短时间内不止一次提交表单。解决方案
一般情况下,是在服务器利用 session 来防止这个问题的。
流程图:
1. 网页点击事件,网页提交发送申请;
2. 服务器收到申请,并产生令牌(Token),并存于 Session 中;
3. 服务器将令牌返回给页面,页面将令牌与表单真正提交给服务器。
这种就是 structs 的令牌方式。还有其他方法,就是重定向方法或设置页面过期(前端部分不太了解),不过还是感觉强制跳转不是特别友好,同时也不够灵活多用。
前期准备
新建一个 spring boot 项目(建议 1.3.X 以上版本)。
加入 aop 依赖,默认设置就行了:
1
2
3
4
|
<dependency>
<groupid>org.springframework.boot</groupid>
spring-boot-starter-aop</artifactid>
</dependency>
|
正式开工
注解类 Token.java
1
2
3
4
5
6
7
8
9
|
@Retention
(RetentionPolicy.RUNTIME)
@Target
(ElementType.METHOD)
@Documented
public
@interface
Token {
//生成 Token 标志
boolean
save()
default
false
;
//移除 Token 值
boolean
remove()
default
false
;
}
|
1
2
3
4
5
6
|
public
class
FormRepeatException
extends
RuntimeException {
public
FormRepeatException(String message){
super
(message);}
public
FormRepeatException(String message, Throwable cause){
super
(message, cause);}
}
|
注意:@Aspect与@Component两个注解!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
@Aspect
@Component
public
class
TokenContract {
private
static
final
Logger logger = LoggerFactory.getLogger(TokenContract.
class
);
@Before
(
"within(@org.springframework.stereotype.Controller *) && @annotation(token)"
)
public
void
testToken(
final
JoinPoint joinPoint, Token token){
try
{
if
(token !=
null
) {
//获取 joinPoint 的全部参数
Object[] args = joinPoint.getArgs();
HttpServletRequest request =
null
;
HttpServletResponse response =
null
;
for
(
int
i =
0
; i < args.length; i++) {
//获得参数中的 request && response
if
(args[i]
instanceof
HttpServletRequest) {
request = (HttpServletRequest) args[i];
}
if
(args[i]
instanceof
HttpServletResponse) {
response = (HttpServletResponse) args[i];
}
}
boolean
needSaveSession = token.save();
if
(needSaveSession){
String uuid = UUID.randomUUID().toString();
request.getSession().setAttribute(
"token"
, uuid);
logger.debug(
"进入表单页面,Token值为:"
+uuid);
}
boolean
needRemoveSession = token.remove();
if
(needRemoveSession) {
if
(isRepeatSubmit(request)) {
logger.error(
"表单重复提交"
);
throw
new
FormRepeatException(
"表单重复提交"
);
}
request.getSession(
false
).removeAttribute(
"token"
);
}
}
}
catch
(FormRepeatException e){
throw
e;
}
catch
(Exception e){
logger.error(
"token 发生异常 : "
+e);
}
}
private
boolean
isRepeatSubmit(HttpServletRequest request)
throws
FormRepeatException {
String serverToken = (String) request.getSession(
false
).getAttribute(
"token"
);
if
(serverToken ==
null
) {
//throw new FormRepeatException("session 为空");
return
true
;
}
String clinetToken = request.getParameter(
"token"
);
if
(clinetToken ==
null
|| clinetToken.equals(
""
)) {
//throw new FormRepeatException("请从正常页面进入!");
return
true
;
}
if
(!serverToken.equals(clinetToken)) {
//throw new FormRepeatException("重复表单提交!");
return
true
;
}
logger.debug(
"校验是否重复提交:表单页面Token值为:"
+clinetToken +
",Session中的Token值为:"
+serverToken);
return
false
;
}
}
|
访问 https://localhost:8080/savetoken 来获得令牌值
访问 https://localhost:8080/removetoken?token=XXX 来提交真正的表单
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Token
(save =
true
)
@RequestMapping
(
"/savetoken"
)
@ResponseBody
public
String getToken(HttpServletRequest request, HttpServletResponse response){
return
(String) request.getSession().getAttribute(
"token"
);
}
@Token
(remove =
true
)
@RequestMapping
(
"/removetoken"
)
@ResponseBody
public
String removeToken(HttpServletRequest request, HttpServletResponse response){
return
"success"
;
}
|