五、WEB开发(二)

6、RestfulCRUD

1)、默认访问首页

//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
//@EnableWebMvc   不要接管SpringMVC
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
       // super.addViewControllers(registry);
        //浏览器发送 /atguigu 请求来到 success
        registry.addViewController("/atguigu").setViewName("success");
    }
    //所有的WebMvcConfigurerAdapter组件都会一起起作用
    @Bean //将组件注册在容器
    public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
        WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
            @Override
            public void addViewControllers(ViewControllerRegistry registry) {
                registry.addViewController("/").setViewName("login");
                registry.addViewController("/index.html").setViewName("login");
            }
        };
        return adapter;
    }
}

2)、国际化

  1. 编写国际化配置文件;
  2. 使用ResourceBundleMessageSource管理国际化资源文件
  3. 在页面使用fmt:message取出国际化内容

步骤:

  1. 编写国际化配置文件,抽取页面需要显示的国际化消息
  2. SpringBoot自动配置好了管理国际化资源文件的组件;
    @ConfigurationProperties(prefix = "spring.messages")
    public class MessageSourceAutoConfiguration {
        /**
      * Comma-separated list of basenames (essentially a fully-qualified classpath
      * location), each following the ResourceBundle convention with relaxed support for
      * slash based locations. If it doesn't contain a package qualifier (such as
      * "org.mypackage"), it will be resolved from the classpath root.
      */
     private String basename = "messages";  
        //我们的配置文件可以直接放在类路径下叫messages.properties;
        @Bean
     public MessageSource messageSource() {
       ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
       if (StringUtils.hasText(this.basename)) {
                //设置国际化资源文件的基础名(去掉语言国家代码的)
         messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
             StringUtils.trimAllWhitespace(this.basename)));
       }
       if (this.encoding != null) {
         messageSource.setDefaultEncoding(this.encoding.name());
       }
       messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale);
       messageSource.setCacheSeconds(this.cacheSeconds);
       messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat);
       return messageSource;
     }
    
  3. 去页面获取国际化的值;
    <!DOCTYPE html>
    <html lang="en"  xmlns:th="http://www.thymeleaf.org">
     <head>
       <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
       <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
       <meta name="description" content="">
       <meta name="author" content="">
       <title>Signin Template for Bootstrap</title>
       <!-- Bootstrap core CSS -->
       <link href="asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.0.0/css/bootstrap.css}" rel="stylesheet">
       <!-- Custom styles for this template -->
       <link href="asserts/css/signin.css" th:href="@{/asserts/css/signin.css}" rel="stylesheet">
     </head>
     <body class="text-center">
       <form class="form-signin" action="dashboard.html">
         <img class="mb-4" th:src="@{/asserts/img/bootstrap-solid.svg}" src="asserts/img/bootstrap-solid.svg" alt="" width="72" height="72">
         <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
         <label class="sr-only" th:text="#{login.username}">Username</label>
         <input type="text" class="form-control" placeholder="Username" th:placeholder="#{login.username}" required="" autofocus="">
         <label class="sr-only" th:text="#{login.password}">Password</label>
         <input type="password" class="form-control" placeholder="Password" th:placeholder="#{login.password}" required="">
         <div class="checkbox mb-3">
           <label>
                 <input type="checkbox" value="remember-me"/> [[#{login.remember}]]
            </label>
         </div>
         <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>
         <p class="mt-5 mb-3 text-muted">© 2017-2018</p>
         <a class="btn btn-sm">中文</a>
         <a class="btn btn-sm">English</a>
       </form>
     </body>
    </html>
    
    效果:根据浏览器语言设置的信息切换了国际化; 原理:国际化Locale(区域信息对象);LocaleResolver(获取区域信息对象);
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
    public LocaleResolver localeResolver() {
      if (this.mvcProperties
          .getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
        return new FixedLocaleResolver(this.mvcProperties.getLocale());
      }
      AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
      localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
      return localeResolver;
    }
    默认的就是根据请求头带来的区域信息获取Locale进行国际化
    
  4. 点击链接切换国际化
    /**
     * 可以在连接上携带区域信息
     */
    public class MyLocaleResolver implements LocaleResolver {
        @Override
        public Locale resolveLocale(HttpServletRequest request) {
            String l = request.getParameter("l");
            Locale locale = Locale.getDefault();
            if(!StringUtils.isEmpty(l)){
                String[] split = l.split("_");
                locale = new Locale(split[0],split[1]);
            }
            return locale;
        }
        @Override
        public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
        }
    }
     @Bean
        public LocaleResolver localeResolver(){
            return new MyLocaleResolver();
        }
    }
    

3)、登陆

开发期间模板引擎页面修改以后,要实时生效

  1. 禁用模板引擎的缓存
    # 禁用缓存
     spring.thymeleaf.cache=false
    
  2. 页面修改完成以后ctrl+f9:重新编译;
    <!--登陆错误消息的显示-->
    <p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
    

4)、拦截器进行登陆检查

拦截器

 /**
  * 登陆检查,
  */
 public class LoginHandlerInterceptor implements HandlerInterceptor {
     //目标方法执行之前
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
         Object user = request.getSession().getAttribute("loginUser");
         if(user == null){
             //未登陆,返回登陆页面
             request.setAttribute("msg","没有权限请先登陆");
             request.getRequestDispatcher("/index.html").forward(request,response);
             return false;
         }else{
             //已登陆,放行请求
             return true;
         }
 
     }
 
     @Override
     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
 
     }
 
     @Override
     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
 
     }
 }
 

注册拦截器

//所有的WebMvcConfigurerAdapter组件都会一起起作用
@Bean //将组件注册在容器
public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
  WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
      registry.addViewController("/").setViewName("login");
      registry.addViewController("/index.html").setViewName("login");
      registry.addViewController("/main.html").setViewName("dashboard");
     }
    //注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
      //super.addInterceptors(registry);
      //静态资源;  *.css , *.js
      //SpringBoot已经做好了静态资源映射
      registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
           .excludePathPatterns("/index.html","/","/user/login");
     }
   };
  return adapter;
 }

5)、CRUD-员工列表

实验要求:

1)、RestfulCRUD:CRUD满足Rest风格;URI:/资源名称/资源标识;HTTP请求方式区分对资源CRUD操作

普通CRUD(uri来区分操作) RestfulCRUD
查询 getEmp emp---GET
添加 addEmp?xxx emp---POST
修改 updateEmp?id=xxx&xxx=xx emp/{id}---PUT
删除 deleteEmp?id=1 emp/{id}---DELETE

2)、实验的请求架构;

实验功能 请求URI 请求方式
查询所有员工 emps GET
查询某个员工(来到修改页面) emp/1 GET
来到添加页面 emp GET
添加员工 emp POST
来到修改页面(查出员工进行信息回显) emp/1 GET
修改员工 emp PUT
删除员工 emp/1 DELETE

3)、员工列表:

thymeleaf公共页面元素抽取

 1、抽取公共片段
 <div th:fragment="copy">
 © 2011 The Good Thymes Virtual Grocery
 </div>
 
 2、引入公共片段
 <div th:insert="~{footer :: copy}"></div>
 ~{templatename::selector}:模板名::选择器
 ~{templatename::fragmentname}:模板名::片段名
 
 3、默认效果:
 insert的公共片段在div标签中
 如果使用th:insert等属性进行引入,可以不用写~{}:
 行内写法可以加上:[[~{}]];[(~{})];

三种引入公共片段的th属性:

  • th:insert:将公共片段整个插入到声明引入的元素中
  • th:replace:将声明引入的元素替换为公共片段
  • th:include:将被引入的片段的内容包含进这个标签中
 <footer th:fragment="copy">
 © 2011 The Good Thymes Virtual Grocery
 </footer>
 
 引入方式
 <div th:insert="footer :: copy"></div>
 <div th:replace="footer :: copy"></div>
 <div th:include="footer :: copy"></div>
 
 效果
 <div>
     <footer>
     © 2011 The Good Thymes Virtual Grocery
     </footer>
 </div>
 
 <footer>
 © 2011 The Good Thymes Virtual Grocery
 </footer>
 
 <div>
 © 2011 The Good Thymes Virtual Grocery
 </div>

引入片段的时候传入参数:

  <nav class="col-md-2 d-none d-md-block bg-light sidebar" id="sidebar">
     <div class="sidebar-sticky">
         <ul class="nav flex-column">
             <li class="nav-item">
                 <a class="nav-link active"
                    th:class="${activeUri=='main.html'?'nav-link active':'nav-link'}"
                    href="#" th:href="@{/main.html}">
                     <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home">
                         <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
                         <polyline points="9 22 9 12 15 12 15 22"></polyline>
                     </svg>
                     Dashboard <span class="sr-only">(current)</span>
                 </a>
             </li>
 
 <!--引入侧边栏;传入参数-->
 <div th:replace="commons/bar::#sidebar(activeUri='emps')"></div>

6)、CRUD-员工添加

添加页面

 <form>
     <div class="form-group">
         <label>LastName</label>
         <input type="text" class="form-control" placeholder="zhangsan">
     </div>
     <div class="form-group">
         <label>Email</label>
         <input type="email" class="form-control" placeholder="zhangsan@atguigu.com">
     </div>
     <div class="form-group">
         <label>Gender</label><br/>
         <div class="form-check form-check-inline">
             <input class="form-check-input" type="radio" name="gender"  value="1">
             <label class="form-check-label">男</label>
         </div>
         <div class="form-check form-check-inline">
             <input class="form-check-input" type="radio" name="gender"  value="0">
             <label class="form-check-label">女</label>
         </div>
     </div>
     <div class="form-group">
         <label>department</label>
         <select class="form-control">
             <option>1</option>
             <option>2</option>
             <option>3</option>
             <option>4</option>
             <option>5</option>
         </select>
     </div>
     <div class="form-group">
         <label>Birth</label>
         <input type="text" class="form-control" placeholder="zhangsan">
     </div>
     <button type="submit" class="btn btn-primary">添加</button>
 </form>
  • 提交的数据格式不对:生日:日期;
  • 2017-12-12;2017/12/12;2017.12.12;
  • 日期的格式化;SpringMVC将页面提交的值需要转换为指定的类型;
  • 2017-12-12---Date; 类型转换,格式化;
  • 默认日期是按照/的方式;

7)、CRUD-员工修改

修改添加二合一表单

 <!--需要区分是员工修改还是添加;-->
 <form th:action="@{/emp}" method="post">
     <!--发送put请求修改员工数据-->
     <!--
 1、SpringMVC中配置HiddenHttpMethodFilter;(SpringBoot自动配置好的)
 2、页面创建一个post表单
 3、创建一个input项,name="_method";值就是我们指定的请求方式
 -->
     <input type="hidden" name="_method" value="put" th:if="${emp!=null}"/>
     <input type="hidden" name="id" th:if="${emp!=null}" th:value="${emp.id}">
     <div class="form-group">
         <label>LastName</label>
         <input name="lastName" type="text" class="form-control" placeholder="zhangsan" th:value="${emp!=null}?${emp.lastName}">
     </div>
     <div class="form-group">
         <label>Email</label>
         <input name="email" type="email" class="form-control" placeholder="zhangsan@atguigu.com" th:value="${emp!=null}?${emp.email}">
     </div>
     <div class="form-group">
         <label>Gender</label><br/>
         <div class="form-check form-check-inline">
             <input class="form-check-input" type="radio" name="gender" value="1" th:checked="${emp!=null}?${emp.gender==1}">
             <label class="form-check-label">男</label>
         </div>
         <div class="form-check form-check-inline">
             <input class="form-check-input" type="radio" name="gender" value="0" th:checked="${emp!=null}?${emp.gender==0}">
             <label class="form-check-label">女</label>
         </div>
     </div>
     <div class="form-group">
         <label>department</label>
         <!--提交的是部门的id-->
         <select class="form-control" name="department.id">
             <option th:selected="${emp!=null}?${dept.id == emp.department.id}" th:value="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}">1</option>
         </select>
     </div>
     <div class="form-group">
         <label>Birth</label>
         <input name="birth" type="text" class="form-control" placeholder="zhangsan" th:value="${emp!=null}?${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}">
     </div>
     <button type="submit" class="btn btn-primary" th:text="${emp!=null}?'修改':'添加'">添加</button>
 </form>

8)、CRUD-员工删除

 <tr th:each="emp:${emps}">
     <td th:text="${emp.id}"></td>
     <td>[[${emp.lastName}]]</td>
     <td th:text="${emp.email}"></td>
     <td th:text="${emp.gender}==0?'女':'男'"></td>
     <td th:text="${emp.department.departmentName}"></td>
     <td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}"></td>
     <td>
         <a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.id}">编辑</a>
         <button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">删除</button>
     </td>
 </tr>
 
 
 <script>
     $(".deleteBtn").click(function(){
         //删除当前员工的
         $("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();
         return false;
     });
 </script>