一,添加页面

问题一:

如果直接跳转到页面上,无法取得在session域内的值。

解决方法:需要跳转到后台页面用方法获取后跳转到指定的添加页面

spring boot topicPattern 使用 spring boot curd_服务器

@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

spring boot topicPattern 使用 spring boot curd_spring_02

解决方案:

在方法的实体类前面添加@Valid ,在后面添加BindingResult bindingResult

spring boot topicPattern 使用 spring boot curd_表单_03

问题三:添加页面时,日期转换问题

解决方法:在下面类中添加Converter类

spring boot topicPattern 使用 spring boot curd_spring_04

@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.

spring boot topicPattern 使用 spring boot curd_服务器_05

spring boot topicPattern 使用 spring boot curd_表单_06

spring boot topicPattern 使用 spring boot curd_表单_07

spring boot topicPattern 使用 spring boot curd_服务器_08


 问题五: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 依赖,默认设置就行了:


?


<dependency>        

                  <groupid>org.springframework.boot</groupid>        

                  spring-boot-starter-aop</artifactid>        

         </dependency>        

    


正式开工

注解类 Token.java

?


@Retention         (RetentionPolicy.RUNTIME)        

         @Target         (ElementType.METHOD)        

         @Documented        

         public         @interface         Token {        

                  //生成 Token 标志        

                  boolean         save()          default         false         ;        

                  //移除 Token 值        

                  boolean         remove()          default         false         ;        

         }        

表单异常类 FormRepeatException.java

?


public         class         FormRepeatException          extends         RuntimeException {        

                  

                  public         FormRepeatException(String message){          super         (message);}        

                  

                  public         FormRepeatException(String message, Throwable cause){          super         (message, cause);}        

         }        

拦截器 TokenContract.java


注意:@Aspect与@Component两个注解!

?


@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         ;        

                  }        

         }        

Controller类


访问 https://localhost:8080/savetoken 来获得令牌值


访问 https://localhost:8080/removetoken?token=XXX 来提交真正的表单

?


@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"       ;      

       }