• 08、SpringBoot的web开发(Restful的CRUD)


    项目总体结构

    项目下载地址https://oss.coydone.com/md/resources/restful-crud.zip

    项目资料(静态文件、实体类、dao类):https://oss.coydone.com/md/resources/sgg_crud.zip

    项目初始化

    thymeleaf模板支持

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
        <version>2.2.7.RELEASE</version>
    </dependency>
    

    静态资源导入

    将前端的css、img、js资源导入到springboot工程的rescources/static目录下。

    html页面导入到rescources/templates目录下。

    将java代码的实体类导入到pojo包下,将dao层java类导入到dao包下。新建config包用于编写配置类,新建controller包用于编写控制层。

    修改html页面资源引入

    <!--让页面支持thymeleaf模板-->
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    
    <!-- 修改css导入路径 -->
    <!-- Bootstrap core CSS -->
    <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
    <!-- Custom styles for this template -->
    <link th:href="@{/css/signin.css}" rel="stylesheet">
    

    编写控制层代码映射到index.html页面

    @Controller
    public class IndexController {
        @RequestMapping({"/","/index.html"})
        public String index(){
            return "index";
        }
    }
    

    也可以自定义配置类,在配置类中控制访问(推荐)

    package com.coydone.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
    
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            //访问 /   或 index,html  都跳转到index.html
            registry.addViewController("/").setViewName("index");
            registry.addViewController("/index.html").setViewName("index");
        }
    }
    
    

    国际化

    为防止配置文件.properties乱码问题,选择utf-8编码。

    properties编码设置

    在rescources下新建国际化目录i18n,新建login.properties、login_zh_CN.properties、login_en_US.properties。

    编写国际化配置

    此时建议将html页面的默认语言该为中文。lang="zh-CN"

    <html lang="en"></html>//英文
    <html lang="zh-CN"></html>//中文
    <html lang="ja"></html>//日文
    <html lang="en-US"></html>//美式英文
    

    SpringBoot自动配置好了管理国际化资源文件的组件:如下源码

    @ConfigurationProperties(prefix = "spring.messages")
    public class MessageSourceAutoConfiguration {
    	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)));
    		}
    		......
    	}
    

    在页面上获取国际化的值,修改index.html

    <!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 th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
    		<!-- Custom styles for this template -->
    		<link th:href="@{/css/signin.css}" rel="stylesheet">
    	</head>
    
    	<body class="text-center">
    		<form class="form-signin" th:action="@{/user/login}" method="post">
    			<img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72">
    			<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}"></h1>
    			<p style="color: red;" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
    			<label class="sr-only" th:text="#{login.username}"></label>
    			<input type="text" class="form-control" name="username" th:placeholder="#{login.username}" required="" autofocus="">
    			<label class="sr-only"  th:text="#{login.password}"></label>
    			<input type="password" name="password" class="form-control" th:placeholder="#{login.password}" required="">
    			<div class="checkbox mb-3">
    				<label>
              <input type="checkbox" value="remember-me" th:text="#{login.remember}">
            </label>
    			</div>
    			<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}"></button>
    			<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
    			<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>
    		</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进行国际化
    

    我们在配置文件中加入locale所在的包即可

    # 我们配置文件的真实位置
    spring.messages.basename=i18n.login
    

    编写config配置类判定中英文选择

    package com.coydone.config;
    
    public class MyLocaleResolver implements LocaleResolver {
        //解析请求
        @Override
        public Locale resolveLocale(HttpServletRequest request) {
            //获取请求中的语言参数
            String language = request.getParameter("l");
            Locale locale = Locale.getDefault();//如果没有就使用默认的
            //如果请求的链接携带了国际化参数
            if (!StringUtils.isEmpty(language)){
                //zh_CN
                String[] split = language.split("_");
                //国家,地区
                locale = new Locale(split[0], split[1]);
            }
            return locale;
        }
        @Override
        public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
        }
    }
    

    登录、拦截器

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

    禁用模板引擎的缓存

    # 禁用缓存
    spring.thymeleaf.cache=false 
    

    页面修改完成以后ctrl+f9:重新编译;

    登陆错误消息的显示

    <p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
    

    设置登录拦截器

    @Controller
    public class LoginController {
        @RequestMapping("/user/login")
        public String login(@RequestParam("username") String username,
                            @RequestParam("password") String password,
                            Model model, HttpSession session){
            if (!StringUtils.isEmpty(username) && "123456".equals(password)){
                //登录用户名随意,密码必须为123456
                session.setAttribute("loginUser",username);
                return "redirect:/main.html";
            }else {
                //登录失败
                model.addAttribute("msg","用户名或密码错误!");
                return "index";
            }
        }
    }
    

    配置拦截器

    package com.coydone.config;
    
    public class LoginHandlerInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            //登录成功之后,应该有用户的session
            Object loginUser = request.getSession().getAttribute("loginUser");
            if (loginUser==null){//没有登录
                request.setAttribute("msg","没有权限,请先登录");
                request.getRequestDispatcher("/index.html").forward(request,response);
                return false;
            }else {
                return true;
            }
        }
    }
    

    公共页面抽取

    thymeleaf公共页面元素抽取

    1、抽取公共片段
    <div th:fragment="copy">
    &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">
    &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>
        &copy; 2011 The Good Thymes Virtual Grocery
        </footer>
    </div>
    
    <footer>
    &copy; 2011 The Good Thymes Virtual Grocery
    </footer>
    
    <div>
    &copy; 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>
    
    <!--引入侧边栏;传入参数  此时通过id属性引入-->
    <div th:replace="commons/bar::#sidebar(activeUri='emps')"></div>
    

    我们需要新建commons/commons.html,把主页的头部和侧边栏分离出来。

    CRUD员工列表

    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
    实验功能 请求URI 请求方式
    查询所有员工 emps GET
    查询某个员工(来到修改页面) emp/{id} GET
    来到添加页面 emp GET
    添加员工 emp POST
    来到修改页面(查出员工进行信息回显) emp/{id} GET
    修改员工 emp PUT
    删除员工 emp/{id} DELETE

    添加员工html页面

    <div class="container-fluid">
        <div class="row">
            <div th:replace="~{commons/commons::sidebar(active='list.html')}"></div>
            <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
                <h2>Section title</h2>
                <form th:action="@{/emp}" method="post">
                    <div class="form-group">
                        <label>LastName</label>
                        <input type="text" name="lastName" class="form-control" placeholder="zhangsan">
                    </div>
                    <div class="form-group">
                        <label>Email</label>
                        <input type="email" name="email" class="form-control" placeholder="zhangsan@coydone.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" name="department.id">
                            <option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
    
                        </select>
                    </div>
                    <div class="form-group">
                        <label>Birth</label>
                        <input type="text" name="birth" class="form-control" placeholder="2020-2-2">
                    </div>
                    <button type="submit" class="btn btn-primary">添加</button>
                </form>
            </main>
        </div>
    </div>
    

    其修改页面与添加类似,只是在业务处理的时候需要把修改的用户信息回显到修改页面,其正确做法是在html页面中添加一个隐藏域,通过id主键获取用户的信息。

    添加修改二合一页面处理

    控制器

    package com.coydone.controller;
    
    @Controller
    public class EmployeeController {
        @Autowired
        EmployeeDao employeeDao;
        @Autowired
        DepartmentDao departmentDao;
        @RequestMapping("/emps")
        public String list(Model model){
            Collection<Employee> employees = employeeDao.getAll();
            model.addAttribute("emps",employees);
            return "emp/list";
        }
    
        @GetMapping("/emp")
        public String toAddpage(Model model){
            //查出所有部门的信息
            Collection<Department> departments = departmentDao.getDepartments();
            model.addAttribute("departments",departments);
            return "emp/add";
        }
    
        @PostMapping("/emp")
        public String addEmp(Employee employee){
            //添加的操作
            employeeDao.save(employee);
            return "redirect:/emps";
        }
        //去员工的修改页面
        @GetMapping("/emp/{id}")
        public String toUpdateEmp(@PathVariable("id")Integer id,Model model){
            //查出原来的数据
            Employee employee = employeeDao.get(id);
            model.addAttribute("emp",employee);
            //查出所有部门的信息
            Collection<Department> departments = departmentDao.getDepartments();
            model.addAttribute("departments",departments);
    
            return "emp/update";
        }
        @PostMapping("/updateEmp")
        public String updateEmp(Employee employee){
            employeeDao.save(employee);
            return "redirect:/emps";
        }
    
        //删除员工
        @GetMapping("/delemp/{id}")
        public String deleteEmp(@PathVariable("id")Integer id){
            employeeDao.delete(id);
            return "redirect:/emps";
        }
        //登出
        @RequestMapping("/user/logout")
        public String logout(HttpSession session){
            session.invalidate();
            return "redirect:/index.html";
        }
    }
    
    coydone的博客
  • 相关阅读:
    SpringBoot入门系列
    日志收集系统-多线程消息队列
    阿里云ecs 服务器配置
    MySQL 表分区详解MyiSam引擎和InnoDb 区别(实测)
    Redis 3.2 Linux 环境集群搭建与java操作
    Java
    多线程编程-工具篇-BlockingQueue
    java常见面试题及答案 11-20(JVM篇)
    28.function_score自定义相关度分数算法
    27.四种常见的相关度分数优化方法
  • 原文地址:https://www.cnblogs.com/coydone/p/13821227.html
Copyright © 2020-2023  润新知