15.springboot的应用示例

15.springboot的应用示例

3种做法:该项目使用了thymeleaf模板,所以会去templates文件夹下去找index.html
    1.后台写个controler
        @Controller
        public class HelloControl {
           @RequestMapping({"/","/index.html"})
            public String login(){
               return "index";
           }
        }
2.写个配置类:写个配置类继承WebMvcConfigurerAdapter ,重写里面的addViewControllers方法
    @Configuration
    public class Myconfg extends WebMvcConfigurerAdapter {
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            //localhost:8080直接跳转到index.html
            registry.addViewController("/").setViewName("index");
            //localhost:8080/index.html也跳转到index.html
            registry.addViewController("/index.html").setViewName("index");
        }
    }
    
3.写个配置类:自己创建个WebMvcConfigurerAdapter 加载到容器中
//由之前的源码得知:所有的WebMvcConfigurerAdapter是一起起作用的,所有我们可以创建个WebMvcConfigurerAdapter放入容器中
@Configuration
    public class Myconfg{
        @Bean
        public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
            WebMvcConfigurerAdapter adapter=new WebMvcConfigurerAdapter() {
                @Override
                public void addViewControllers(ViewControllerRegistry registry) {
                    //localhost:8080直接跳转到index.html
                    registry.addViewController("/").setViewName("index");
                    //localhost:8080/index.html也跳转到index.html
                    registry.addViewController("/index.html").setViewName("index");
                }
            };
            return adapter;
        }
    }
2.thymeleaf标签中的th:href的好处:

html用th:href标签覆盖了原有的href标签,这样的好处
<!-- Bootstrap core CSS -->
<link href="/asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.5.2/css/bootstrap.css}" rel="stylesheet">
<!-- Custom styles for this template -->
<link th:href="@{/asserts/css/signin.css}" href="/asserts/css/signin.css" rel="stylesheet">

1.例如重新设置了项目的访问路径:在application.properties配置中配置
    server.servlet.context-path=/crud
     这时:通过localhost:8080无法定位到登录页面
    需要通过:localhost:8080/crud去访问
    分析下面浏览器登录页面源码得知:thymeleaf会自动帮我们拼接上项目的访问路径:crud

登录页面的国际化功能
登录页面如下:

步骤:
    1.编写国际化配置文件,抽取页面需要显示的国际化消息:如上图所示
        1.1默认显示的配置文件login.properties
            login.tip=请登录-
            login.username=用户名-
            login.password=密码-
            login.remember=记住我-
            login.btn=登录-
        1.2英文显示的配置文件:login_en_US.properties
            login.tip=Please sign in
            login.username=Username
            login.password=Password
            login.remember=Remember me
            login.btn=Sign in
        1.3中文显示的配置文件:login_zh_CN.properties
            login.tip=请登录
            login.username=用户名
            login.password=密码
            login.remember=记住我
            login.btn=登录
      
    2.配置自定义国际化配置文件的路径      
        国际化自动加载的代码:MessageSourceAutoConfiguration
        springboot自动配置好了管理国际化资源文件的组件:
            源码如下:
                public class MessageSourceAutoConfiguration {
                    ...
                    @Bean
                    public MessageSource messageSource(MessageSourceProperties properties) {
                       ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
                       if (StringUtils.hasText(properties.getBasename())) {
                           //设置国际化资源文件的基础名(去掉语言国家代码的):properties.getBasename()结果是:message
                          messageSource.setBasenames(StringUtils
                          //得到的结论是:我们的配置文件可以直接放在类路径下交message.properties
                                .commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
                       }
                       if (properties.getEncoding() != null) {
                          messageSource.setDefaultEncoding(properties.getEncoding().name());
                       }
                       messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
                       Duration cacheDuration = properties.getCacheDuration();
                       if (cacheDuration != null) {
                          messageSource.setCacheMillis(cacheDuration.toMillis());
                       }
                       messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
                       messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
                       return messageSource;
                    }
                }
                
        我们自己定义了国际化文件,所以需要在springboot的配置文件中:application.properties指定自定义国际化文件的路径,后面只需要写到基础名即可!!
        spring.messages.basename=i18n.login
        
    3.去页面上取值
        <body class="text-center">
           <form class="form-signin" action="dashboard.html">
              <img class="mb-4" src="/asserts/img/bootstrap-solid.svg" th:src="@{/asserts/img/bootstrap-solid.svg}" alt="" width="72" height="72">
              <!--使用th:text="#{国际化文件中的名称}"去获取国际化配置显示,template模板通过#{}去获取国际化配置文件中的参数值-->
              <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
              <!--使用th:text="#{login.username}替换用户名-->
              <label class="sr-only" th:text="#{login.username}">Username</label>
              <!--使用th:placeholder="#{login.username}"替换默认的placeholder="Username"-->
              <input type="text" class="form-control" th:placeholder="#{login.username}" placeholder="Username" required="" autofocus="">
              <label class="sr-only" th:text="#{login.password}">Password</label>
              <input type="password" class="form-control" th:placeholder="#{login.password}" placeholder="Password" required="">
              <div class="checkbox mb-3">
                 <label>
                 <!--注意这里的input是选框,不能使用th:text替换,而应该使用行内显示[[#{}]]的形式去取国际化配置文件的值-->
                 <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>
1.如果浏览器默认英语在上面则页面显示英文

1.如果浏览器默认中文在上面则页面显示中文

3.如何设置点击页面剩的中文英文实现页面的改变呢

原理:
国际化Locale(区域化信息对象);LocaleResolver (用来获取区域化对象),源码在:WebMvcAutoConfiguration
        @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;
        }
        默认是根据请求头带来的区域信息获取Local进行国际化


实现步骤如下:
    1.页面上:
         原:
         <a class="btn btn-sm">中文</a>
         <a class="btn btn-sm">English</a>
         
         现改为:加上th:href="@{/index.html(l='zh_CN')}发起请求,并出入参数,传入参数的格式是(key=value),传到当前页面!
         <a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
         <a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
    
    2.编写自己的区域化对象:
        public class MyLocaleResolver  implements LocaleResolver {
            @Override
            public Locale resolveLocale(HttpServletRequest request) {
                //获取页面传来的请求参数l
                String l = request.getParameter("l");
                //获取默认的区域化对象
                Locale locale=Locale.getDefault();
                //如果页面传值为空,就用默认的区域化对象
                if(!StringUtils.isEmpty(l)){
                    //不为空,zh_CN或en_US进行"-"分割,0-语言  1-国家
                    String[] split = l.split("_");
                    locale=new Locale(split[0], split[1]);
                }
                return locale;
            }
            @Override
            public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
        
            }
        }
    3.这时自己创建的区域化对象还没有加载到容器中
        所以在自己的配置类中:
        @Configuration
            public class Myconfg extends WebMvcConfigurerAdapter {
                @Override
                public void addViewControllers(ViewControllerRegistry registry) {
                    //localhost:8080直接跳转到index.html
                    registry.addViewController("/").setViewName("index");
                    //localhost:8080/index.html也跳转到index.html
                    registry.addViewController("/index.html").setViewName("index");
                }
                //由之前的源码得知:所有的WebMvcConfigurerAdapter是一起起作用的,所有我们可以创建个WebMvcConfigurerAdapter放入容器中
                @Bean
                public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
                    WebMvcConfigurerAdapter adapter=new WebMvcConfigurerAdapter() {
                        @Override
                        public void addViewControllers(ViewControllerRegistry registry) {
                            //localhost:8080直接跳转到index.html
                            registry.addViewController("/").setViewName("index");
                            //localhost:8080/index.html也跳转到index.html
                            registry.addViewController("/index.html").setViewName("index");
                        }
                    };
                    return adapter;
                }
                //将其加载到容器中
                @Bean
                public LocaleResolver localeResolver(){
                    return new MyLocaleResolver();
                }
            }
这时实现了页面上点击中文显示中文,点击英语显示英语
4.如何实现用户名密码错误提示

设计原理:登录失败后,后台将错误信息放入到map中,前台进行获取
1.html页面表单:以post的形式体提交,路径为:/user/login
    <form class="form-signin" action="dashboard.html" th:action="@{/user/login}" method="post">
        ...
    <form/>
    
2.对应的controler类:
    @Controller
    public class HelloControl {
        //@RequestMapping(value = "/user/login",method = RequestMethod.POST)
        @PostMapping("/user/login")-->和上面的注解效果一致
        //@GetMapping:springboot对rest风格的支持
        //@DeleteMapping
        //@PutMapping
        public String login(@RequestParam("username") String userName, @RequestParam("password") String passWord, HttpSession session,Map<String, Object> map) {
            System.out.println("页面传来的user:" + userName + "  passWord:" + passWord);
            if(!StringUtils.isEmpty(userName)&&"123456".equals(passWord)){
                System.out.println("账户密码正确!");
                //将用户名放入session中
                session.setAttribute("username",userName);
                return "redirect:/main.html";--->通过重定向到该路径:可以解决刷新表单的重复提交问题...
            }else{
                System.out.println("登录失败!用户名或密码错误");
                map.put("msg","用户名密码错误");--->登录失败后,将错误信息放入到map中,供前台获取!
                return "index";
            }
        }
    }
3.重定向的路径到目标页面:
    @Configuration
    public class Myconfg extends WebMvcConfigurerAdapter {
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            //localhost:8080直接跳转到index.html
            registry.addViewController("/").setViewName("index");
            //localhost:8080/index.html也跳转到index.html
            registry.addViewController("/index.html").setViewName("index");
            registry.addViewController("/main.html").setViewName("dashboard");
            ---->发现main.html路径转发到dashboard.html(模板引擎thymeleaf会将dashboard转为dashboard.html,去templates文件夹下找dashboard.html)
        }
        。。。。
    }
问题:直接访问项目资源:http://localhost:8080/main.html可以直接访问,如何让用户在没有登录的情况下访问资源,跳转到登录页面

实现:登录成功后,将用户信息保存到session中,再创建拦截器,拦截用户的访问资源请求,判断用户是否登录
1.登录成功后将用户信息保存到session中
    @PostMapping("/user/login")
    public String login(@RequestParam("username") String userName, @RequestParam("password") String passWord, HttpSession session,Map<String, Object> map) {
        System.out.println("页面传来的user:" + userName + "  passWord:" + passWord);
        if(!StringUtils.isEmpty(userName)&&"123456".equals(passWord)){
            System.out.println("账户密码正确!");
            //将用户名放入session中
            session.setAttribute("username",userName);----->登录成功后,将用户名放入到sesison中
            return "redirect:/main.html";
        }else{
            System.out.println("登录失败!用户名或密码错误");
            map.put("msg","用户名密码错误");
            return "index";
        }
    }

2.创建拦截器拦截用户的访问资源请求
    2.1自建的拦截器
        public class LoginHandlerInterceptor implements HandlerInterceptor {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                Object user = request.getSession().getAttribute("username");---->判断session中是否有用户名
                System.out.println("拦截器获取登录名称:"+user+" 拦截路径:"+request.getRequestURL());
                if (user==null){
                    System.out.println("登录对象为空,请先登录!");
                    request.setAttribute("msg","没有权限访问,请先登录!");--->将错误提示信息放入到request域中,页面获取
                    request.getRequestDispatcher("/index.html").forward(request, response);--->没有的话进行请求转发到登录页面
                    return false;
                }else {
                    return true;---->有的话放行,可以继续访问资源
                }
            }
            ...
        }
    2.2将拦截器加载到容器中
        @Configuration
        public class Myconfg extends WebMvcConfigurerAdapter {
            ...
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                registry.addInterceptor(new LoginHandlerInterceptor())
                        .addPathPatterns("/**")---->拦截所有请求
                        .excludePathPatterns("/","/index.html","/user/login","/asserts/**","/webjars/**");---->哪些路径不拦截
                        //  1./和/index.html不拦截,要不无法打开登录页面
                        //  2./user/login不拦截,要不无法进行登录验证
                        //  3."/asserts/**","/webjars/**"不拦截,要不页面无法加载样式
            }
            ...
        }
        
4.页面获取错误提示数据:
    <p style="color: #ff0000" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>

如何在登录成功页面显示登录名

实现原理:
    获取登录成功后放入session域中的username,并展示在页面
    <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#" th:text="${session.username}">用户名称</a>
REST风格的员工增删改查(crud)
什么是rest风格:通过不同的请求方式(get/post/delete等)去区分对资源的CRUD操作(增删改查)
 
普通的CRUD(通过uri区分操作)
RestFulCRUD
查询
getEmp
emp---GET
添加
addEmp?xxx
emp---POST
修改
updateEmp?id=xxx&xxx=xx
emp/{id}----PUT
删除
deleteEmp?id=xxx
emp/{id}---delete
需求的请求架构
 
请求URI
请求方式
查询所有员工
emps
GET
查询某个员工(来到修改页面)
emp/{id}
GET
来到添加页面
emp
GET
添加员工
emp
POST
来到修改页面(查询员工进行信息回显)
emp/{id}
GET
修改员工
emp
PUT
删除员工
emp/{id}
delete
3.员工列表:thymeleaf公共页面元素抽取
1.抽取公共片段,格式如下:th:fragment="copy"在公用的地方使用这个标签
    <div th:fragment="copy">
        © 2011 The Good Thymes Virtual Grocery
    </div>
    
2.引入公用的片段
    <div th:insert="~{footer :: copy}"></div>
    ~{templatename::selector} :模板名 :: 选择器
    ~{templatename::fragmentname} :模板名 :: 片段名
    
3.三种引入方式:这三种分别是什么效果,示例如下
    th:insert:将公共片段插入到指定声明引入元素中
    th:replace :将声明引入元素替换为公共片段
    th:include:将被引入的片段的内容包含到声明元素中
    示例:
        1.需要引入的代码:
            <footer th:fragment="copy">
                © 2011 The Good Thymes Virtual Grocery
            </footer>
        2.th:insert
            <div th:insert="footer :: copy"></div>
            其效果如下:
            <div>
                <footer>
                © 2011 The Good Thymes Virtual Grocery
                </footer>
            </div>
        2.th:replace
            <div th:replace="footer :: copy"></div>
            其效果如下:
            <footer>
                © 2011 The Good Thymes Virtual Grocery
            </footer>
        3.th:include
            <div th:include="footer :: copy"></div>
            其效果如下:
            <div>
                © 2011 The Good Thymes Virtual Grocery
            </div>
            
            
具体的代码实现:
    在刚登录进来的页面:dashboard.html,定义公共的片段
        使用th:fragment="片段名称"定义公共的片段
        <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
           <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#" th:text="${session.username}">用户名称</a>
           <input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
           <ul class="navbar-nav px-3">
              <li class="nav-item text-nowrap">
                 <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Sign out</a>
              </li>
           </ul>
        </nav>
就是如图的部分

然后在点击左侧员工列表的list.html引入,并且insert标签会替换掉div标签,这样和原页面(dashboard.html)就保持了一致
    引入抽取的topbar,格式为: 模板名 :: 公共片段名  模板名:会使用thymeleaf的前后缀配置规则进行解析
    <div th:insert="dashboard::topbar"></div>
如果是insert标签:
    原格式是:~{templatename::fragmentname}
    可以省略~和大括号:templatename::fragmentname
这样实现了头部的公用,同理可以实现左侧导航栏的公用

侧边栏的引入:
    原页面:加了一个id,其他并没有变
        <nav class="col-md-2 d-none d-md-block bg-light sidebar" id="sidebar">
            ...
        <nav>
    目标页面:通过使用#id的形式去导入公共页面
        <div  th:insert="~{dashboard :: #sidebar}"></div>
问题:如何让点击哪个侧边栏,哪个侧边栏高亮

实现原理:
    发现高亮的元素的class为:class="nav-link active" 
    不高亮的元素的class为:class="nav-link" 
    没有active
并且:目标页面在引入公共片段时可以传入参数
所以实现原理是:每个元素判断目标页面传来的参数是不是自己的路径,不是就不加active,是加上active

将头部栏和侧边栏抽取出到templates文件夹下的common/commmonBar.html
示例如下:
    1.在首页页面(dashboard.html)引入侧边栏,并传入参数:activeUrl='/main.html'即为首页页面的访问地址/main.html
    自定义的视图控制器:registry.addViewController("/main.html").setViewName("dashboard");会将main.html转到目标页面:dashboard.html
        <div th:replace="~{common/commonBar :: #sidebar(activeUrl='/main.html')}"></div>
        传入多个参数值的话,用逗号隔开
    2.在员工列表页面(list.html)
        <div th:replace="~{common/commonBar :: #sidebar(activeUrl='/emps')}"></div>
        传入员工列表的访问路径!作为区别参数
    3.在抽取出的公共页面中进行路径判断,以高亮不同显示!
        <a class="nav-link active" href="#" 
            th:href="@{/main.html}" 
            这里进行三目运算:如果不是自己的首页路径,就不加active属性,也就不会高亮显示
            th:class="${activeUrl=='/main.html'?'nav-link active':'nav-link'}">
            首页
        <a/>
        员工列表同理:
        <a class="nav-link active" href="#" th:href="@{/emps}" th:class="${activeUrl=='/emps'?'nav-link active':'nav-link'}">
        员工列表
        </a>

结论:以上设置实现了点击哪个标签,哪个标签高亮的操作!
查询出员工并循环取值在页面上显示

1.控制类查询代码:
    @GetMapping("emps")
    public String getEmpsList(Map<String, Object> map){
        Collection<Employee> empsList = employeeDao.getAll();
        map.put("emps",empsList);-->放入map中,key=emps
        //Thymeleaf模板引擎会自动拼串:classpath:/templates/xxx.html,所以会跳转到/templates/emp/list.html页面
        return "emp/list";
    }
2.页面上的代码(list.html)
    <table class="table table-striped table-sm">
       <thead>
          <tr>
             <th>id</th>
             <th>名称</th>
             <th>邮箱</th>
             <th>性别</th>
             <th>部门</th>
             <th>生日</th>
             <th>操作</th>
          </tr>
       </thead>
       <tbody>
          <tr th:each="emp:${emps}">--->获取查询出的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')}"></td>
             <td>
                <button class="btn btn-sm btn-primary">修改</button>
                <button class="btn btn-sm btn-danger">删除</button>
             </td>
          </tr>
       </tbody>
    </table>

posted @ 2022-05-10 21:58  努力的达子  阅读(397)  评论(0编辑  收藏  举报