SpringBoot 学习(三)Web 开发

3. SpringBoot Web 开发

3.1 导入静态资源

(1) webjars

  • 导入 jquery 依赖

    <dependency>
        <groupId>org.webjarsgroupId>
        <artifactId>jqueryartifactId>
        <version>3.6.0version>
    dependency>
    
  • 访问 jquery.js 文件

    http://localhost:8080/webjars/jquery/3.6.0/jquery.js

(2) WebProperties ( SpringBoot 3.5.4 )

  • 静态资源默认路径(访问优先级由高到低排序)

    classpath:/META-INF/resources/
    classpath:/resources/
    classpath:/static/
    classpath:/public/
    

    一般 public 目录存放公共资源,如各模块需要调用的 js;static 目录存放静态资源,如图片;

    resources 目录存放上传的文件。

3.2 配置首页

  • 将 index.html 文件放在静态资源默认路径下,一般放在 static 目录下。
  • 标签页图标 favicon.ico 放在 static 目录下。

3.3 模板引擎

3.3.1 配置模板引擎 Thymeleaf

(1) 导入依赖
<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
(2) 将 html 文件放在对应目录下
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
(3) html 文件引入约束
<html lang="en" xmlns:th="http://www.thymeleaf.org/">

3.3.2 Thymeleaf 语法

  • Tutorial: Using Thymeleaf

3.4 扩展 SpringMVC

  • config 目录下自定义配置类

    // 扩展 SpringMvc,不能加 @EnableWebMvc
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
        @Bean
        public ViewResolver myViewResolver() {
            return new MyViewResolver();
        }
        // 自定义一个视图解析器
        public static class MyViewResolver implements ViewResolver {
            @Override
            public View resolveViewName(String viewName, Locale locale) throws Exception {
                return null;
            }
        }
    }
    

3.5 国际化

(1) 配置 i18n 文件

  • 编写配置文件

    SpringBoot 学习(三)Web 开发_第1张图片

  • 绑定到 html 文件

    SpringBoot 学习(三)Web 开发_第2张图片

(2) 自定义组件

  • 添加 html 国际化按钮

    SpringBoot 学习(三)Web 开发_第3张图片

  • 编写组件

    public class MyLocaleResover 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) {
    
        }
    }
    

(3) 将组件注册到 Spring 中

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
    }

    // 国际化组件注册到Spring
    @Bean
    public LocaleResolver localeResolver() {
        return new MyLocaleResover();
    }
}

(4) 添加配置

# application.properties
# 配置文件位置
spring.messages.basename=i18n.login

3.6 登录实现

(1) 设置 html 输入框 name

<input type="text" class="form-control" name="username" th:placeholder="#{login.username}" required="" autofocus="">
<input type="password" class="form-control" name="password" th:placeholder="#{login.password}" required="">

(2) 设置表单提交路径

<form class="form-signin" th:action="@{/user/login}">

(3) 编写登录控制器

@Controller
public class LoginController {

    @RequestMapping("/user/login")
    public String login(@RequestParam("username") String username,
                        @RequestParam("password") String password,
                        Model model, HttpSession session) {
        // 用户名不为空,密码为 123456
        if (!StringUtils.isEmpty(username) && "123456".equals(password)) {
            // 向 session 传入登录标识
            session.setAttribute("loginUser", username);
            // 重定向到面板
            return "redirect:/main.html";
        } else {
            // 向 msg 传入信息
            model.addAttribute("msg", "用户名或密码错误");
            return "index";
        }
    }
}

(4) 编写登录拦截器

public class LoginHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 获取登录标识
        Object loginUser = request.getSession().getAttribute("loginUser");
        // 没有登录
        if (loginUser == null) {
            // 向 msg 传入信息
            request.setAttribute("msg", "没有权限");
            // 跳转到 index
            request.getRequestDispatcher("/index.html").forward(request, response);
            return false;
        } else {
            return true;
        }
    }
}

(5) 添加登录拦截器

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // / 可访问到 index.html
        registry.addViewController("/").setViewName("index");
        // index.html 可访问到 index
        registry.addViewController("/index.html").setViewName("index");
        // /main.html 可访问到 dashboard
        registry.addViewController("/main.html").setViewName("dashboard");
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 添加登录拦截器
        registry.addInterceptor(new LoginHandlerInterceptor())
                // 设置拦截对象
                .addPathPatterns("/**")
                // 排除拦截对象
                .excludePathPatterns("/index.html", "/", "/user/login","/css/**", "/js/**", "/img/**");
    }
}

3.7 列表展示

3.7.1 提取公共页面

(1) 提取代码

<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
    ...
nav>
<nav class="col-md-2 d-none d-md-block bg-light sidebar"  th:fragment="sidebar">
    ...
nav>
(2) 插入代码

<div th:replace="~{commons/commons::topbar}">div>

<div th:replace="~{commons/commons::sidebar(active='main.html')}">div>

<div th:insert="~{commons/commons::topbar}">div>

<div th:insert="~{commons/commons::sidebar(active='list.html')}">div>
(3) 接收参数
<a th:class="${active == 'main.html'?'nav-link active':'nav-link'}" th:href="@{/index.html}">
    ...
a>
<a th:class="${active == 'list.html'?'nav-link active':'nav-link'}" th:href="@{/emps}">
    ...
a>

3.7.3 渲染列表

(1) 控制器获取数据
@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";
    }
}
(2) 渲染列表
<table class="table table-striped table-sm">
   <thead>
      <tr>
         <th>idth>
         <th>lastNameth>
         <th>emailth>
         <th>genderth>
         <th>departmentth>
         <th>birthth>
         <th>操作th>
      tr>
   thead>
   <tbody>
      <tr th:each="emp:${emps}">
         <td th:text="${emp.getId()}"/>
         <td th:text="${emp.getLastName()}"/>
         <td th:text="${emp.getEmail()}"/>
         <td th:text="${emp.getGender()==0?'女':'男'}"/>
         <td th:text="${emp.getDepartment().departmentName}"/>
         <td th:text="${#dates.format(emp.getBirth(),'YYYY-MM-DD HH:mm:ss')}"/>
         <td>
            <button class="btn btn-sm btn-primary">编辑button>
            <button class="btn btn-sm btn-danger">删除button>
         td>
      tr>
   tbody>
table>

3.8 添加记录

(1) 添加界面

<form th:action="@{/emp}" method="post">
   <div class="form-group">
      <label>LastNamelabel>
      <input type="text" name="lastName" class="form-control" placeholder="why">
   div>
   <div class="form-group">
      <label>Emaillabel>
      <input type="text" name="email" class="form-control" placeholder="[email protected]">
   div>
   <div class="form-group">
      <label>Genderlabel>
      <div class="form-check form-check-inline">
         <input type="radio" class="form-check-input" name="gender" value="0">
         <label class="form-check-label">label>
      div>
      <div class="form-check form-check-inline">
         <input type="radio" class="form-check-input" name="gender" value="1">
         <label class="form-check-label">label>
      div>
   div>
   <div class="form-group">
      <label>Departmentlabel>
      <select class="form-control" name="department.id">
         <option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}">option>
      select>
   div>
   <div class="form-group">
      <label>Birthlabel>
      <input type="text" name="birth" class="form-control" placeholder="2021-01-01">
   div>
   <button type="submit" class="btn btn-primary">添加button>
form>
form>
  • 注意 HTML 元素名称和实体属性名称的对应

(2) 编写控制器

@Controller
public class EmployeeController {

    @Autowired
    EmployeeDao employeeDao;

    @Autowired
    DepartmentDao departmentDao;

    @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) {
        System.out.println("receive_emp ==>" + employee);
        // 保存员工数据
        employeeDao.save(employee);
        return "redirect:/emps";
    }

}

3.9 修改记录

(1) 编写请求链接


<a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.getId()}">编辑a>

(2) 编写页面跳转控制器

// EmployeeController.java
@GetMapping("/emp/{id}")
public String toUpdateEmp(@PathVariable("id") Integer id, Model model) {
    // 获取员工数据
    Employee employee = employeeDao.getEmployeeById(id);
    model.addAttribute("emp", employee);

    // 获取部门数据
    Collection<Department> departments = departmentDao.getDepartments();
    model.addAttribute("departments", departments);

    return "emp/update";
}

(3) 添加修改界面


<form th:action="@{/updateEmp}" method="post">
    
   <input type="hidden" name="id" th:value="${emp.getId()}">
   <div class="form-group">
      <label>LastNamelabel>
      <input th:value="${emp.getLastName()}" type="text" name="lastName" class="form-control" placeholder="why">
   div>
   <div class="form-group">
      <label>Emaillabel>
      <input th:value="${emp.getEmail()}" type="text" name="email" class="form-control" placeholder="[email protected]">
   div>
   <div class="form-group">
      <label>Gender label>
      <div class="form-check form-check-inline">
         <input th:checked="${emp.getGender() == 0}" type="radio" class="form-check-input" name="gender" value="0">
         <label class="form-check-label">label>
      div>
      <div class="form-check form-check-inline">
         <input th:checked="${emp.getGender() == 1}" type="radio" class="form-check-input" name="gender" value="1">
         <label class="form-check-label">label>
      div>
   div>
   <div class="form-group">
      <label>Departmentlabel>
      <select class="form-control" name="department.id">
          
         <option th:selected="${dept.getId() == emp.getDepartment().getId()}" th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}">option>
      select>
   div>
   <div class="form-group">
      <label>Birthlabel>
       
      <input th:value="${#dates.format(emp.getBirth(), 'yyyy-MM-dd HH:mm')}" type="text" name="birth" class="form-control" placeholder="2021-01-01">
   div>
   <button type="submit" class="btn btn-primary">修改button>
form>

(4) 编写修改员工控制器

@PostMapping("/updateEmp")
public String updateEmp(Employee employee) {
    employeeDao.save(employee);
    return "redirect:/emps";
}

3.10 删除记录

(1) 编写请求链接


<a class="btn btn-sm btn-danger" th:href="@{/deleteEmp/}+${emp.getId()}">删除a>

(2) 编写删除控制器

@GetMapping("/deleteEmp/{id}")
public String deleteEmp(@PathVariable("id") Integer id) {
    employeeDao.deleteEmp(id);
    return "redirect:/emps";
}

3.11 错误页面和注销

(1) 错误页面

  • templates 下添加 error 路径

  • 将错误页面以错误代码命名放入 error 目录下

(1) 注销

● 编写链接

<a class="nav-link" th:href="@{/user/logout}">注销a>
● 编写控制器
// LoginController.java
@RequestMapping("/user/logout")
public String logout(HttpSession session) {
    session.invalidate();
    return "redirect:/index.html";
}

你可能感兴趣的:(SpringBoot,spring,boot,学习,前端)