SpringBoot 学习(三)Web 开发

news2024/11/22 13:50:53

3. SpringBoot Web 开发

3.1 导入静态资源

(1) webjars

  • 导入 jquery 依赖

    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>jquery</artifactId>
        <version>3.6.0</version>
    </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.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</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 文件

  • 编写配置文件

    在这里插入图片描述

  • 绑定到 html 文件

    在这里插入图片描述

(2) 自定义组件

  • 添加 html 国际化按钮

    在这里插入图片描述

  • 编写组件

    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) 提取代码
<!--resources/templates/commons/commons.html-->
<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) 插入代码
<!--resources/templates/dashboard.html-->
<div th:replace="~{commons/commons::topbar}"></div>
<!--传递参数-->
<div th:replace="~{commons/commons::sidebar(active='main.html')}"></div>
<!--resources/templates/emp/list.html-->
<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>id</th>
         <th>lastName</th>
         <th>email</th>
         <th>gender</th>
         <th>department</th>
         <th>birth</th>
         <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>LastName</label>
      <input type="text" name="lastName" class="form-control" placeholder="why">
   </div>
   <div class="form-group">
      <label>Email</label>
      <input type="text" name="email" class="form-control" placeholder="123456@qq.com">
   </div>
   <div class="form-group">
      <label>Gender</label>
      <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>Department</label>
      <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>Birth</label>
      <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) 编写请求链接

<!--list.html-->
<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) 添加修改界面

<!--update.html-->
<form th:action="@{/updateEmp}" method="post">
    <!--携带 id 的隐藏域-->
   <input type="hidden" name="id" th:value="${emp.getId()}">
   <div class="form-group">
      <label>LastName</label>
      <input th:value="${emp.getLastName()}" type="text" name="lastName" class="form-control" placeholder="why">
   </div>
   <div class="form-group">
      <label>Email</label>
      <input th:value="${emp.getEmail()}" type="text" name="email" class="form-control" placeholder="123456@qq.com">
   </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>Department</label>
      <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>Birth</label>
       <!--日期格式化-->
      <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) 编写请求链接

<!--list.html-->
<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) 注销

● 编写链接
<!--commons.html-->
<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";
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1041382.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python将中文标点符号转换成英文标点符号然后再替换成回车符实现换行

一段文字如下&#xff1a; 你发现没,杭州亚运会首个比赛日上午&#xff0c;中国体育代表团竟然狂揽11金&#xff01;这一壮丽景象背后&#xff0c;是中国体育事业的坚实基础和精湛训练的见证。 标点符号都是中文状态下的。现在要替换成英文标点符号。参考了文章&#xff1a; …

Linux基本命令总结练习(过命令关)

1.新建网卡配置文件的软连接NIC1 [rootserver ~]# ln /etc/NetworkManager/system-connections/ens160.nmconnection NIC1 [rootserver ~]# stat /etc/NetworkManager/system-connections/ens160.nmconnection [rootserver ~]# stat NIC1 2.使用普通账户新建如下结构的2个目录&…

Vue中如何进行跨域处理

Vue中的跨域请求处理&#xff1a;解决前端开发中的常见问题 跨域请求是前端开发中常见的问题之一。Vue.js是一款流行的前端框架&#xff0c;如何在Vue中处理跨域请求是每个Vue开发者都需要了解的重要课题。本文将深入探讨什么是跨域请求&#xff0c;为什么它会出现&#xff0c…

burpsuite只有intruder中文乱码

burpsuite 只有intruder模块中文乱码 现象&#xff1a;解决方案 现象&#xff1a; 在proxy、repeater等模块下中文均可正常显示&#xff0c;如下图&#xff1a; 在intruder模块&#xff0c;中文显示乱码 解决方案 在payloads标签下payload processing中添加“Decode”

【跟小嘉学 Rust 编程】三十、Rust 使用 Slint UI

系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…

聊聊并发编程——多线程之synchronized

目录 一.多线程下数据不一致问题 二.锁和synchronized 2.1 并发编程三大特性 2.2引入锁概念 三.synchronized的锁实现原理 3.1 monitorenter和monitorexit 3.2synchronized 锁的升级 3.2.1偏向锁的获取和撤销 3.2.2轻量级锁的加锁和解锁 自适应自旋锁 轻量级锁的解锁…

位运算练习题(Java)

package KeepCoding.algorithm; //位运算知识点 /* 1. 0 ^ x x x ^ x 1 * 2. 位运算满足结合律和交换律&#xff0c;即运算顺序无关 */ //位运算练习题 //1.整数数组中有一个出现次数为奇数的整数&#xff0c;其余整数的出现次数均为偶数个&#xff0c;请找出数组中这位…

oracle

title: “Oracle” createTime: 2021-12-13T16:35:4108:00 updateTime: 2021-12-13T16:35:4108:00 draft: false author: “name” tags: [“oracle”] categories: [“db”] description: “测试的” 时间字段分析 timestamp 精确到秒后面6位 createTime: 2021-12-13T16:35:…

微积分学习笔记(2):用Go语言画函数图像

使用的Go库 gonum.org/v1/plotimage/color 待绘图函数 直接使用三角函 s i n sin sin: func f(x float64) float64 {return math.Sin(x) }绘图过程 1 创建一个绘图 p : plot.New()assert.NotNil(t, p)p.Title.Text "Function Image"p.X.Label.Text "X&qu…

基于微信小程序的中医知识库系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言用户微信小程序端的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌…

crypto:Url编码

题目 下载题目所给的压缩包后解压&#xff0c;打开文件可得 由题目可知为url编码&#xff0c;所以使用解码工具解码即可得到flag

【深度学习实验】卷积神经网络(二):实现简单的二维卷积神经网络

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. 二维互相关运算&#xff08;corr2d&#xff09; 2. 二维卷积层类&#xff08;Conv2D&#xff09; a. __init__&#xff08;初始化&#xff09; b. forward(前向传…

React基础教程(四):组件理解

1、函数式组件 实现代码 <script type"text/babel"> /*此处一定要写babel*/ // 1、创建函数式组件 function MyComponent() {console.log(this); // 此处的this是undefined&#xff0c;因为babel编译后开启了严格模式return <h2>我是用函数定义的组件&am…

unable to access xxxx: Failed to connect to xxxx

问题&#xff1a; 1、GitLab仓库加上双重验证后&#xff0c;设置GIt得 Manage Remotes时报错 unable to access xxxx: Failed to connect to xxxx SSL certificate problem:self signed certificate 解决 1、返回前面得操作步骤检查了一遍 没有问题 2、最后尝试一些方法解…

SPA项目之主页面--Mock.js以及组件通信(总线)的运用

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于VueElementUI的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.Mock.js是什么 二.为什么要使用…

跨域的解决方案

文章目录 概念一、什么是跨域问题二、为什么会发生跨域问题三、跨域解决方案1、JSONP2、添加响应头3、Spring注解CrossOrigin4、配置文件&#xff08;常用&#xff09;5、nginx跨域 概念 一、什么是跨域问题 前端调用的后端接口不属于同一个域&#xff08;域名或端口不同&…

Docker 容器监控之CAdvisor+InfluxDB+Granfana

是什么 一句话&#xff1a;CAdvisor监控收集InfluxDB存储数据Granfana展示图表 CAdvisor InfluxDB Granfana 总结 容器编排CIG CIG CAdvisorInfluxDBGranfana 1、新建目录 2、新建docker-compose.yml文件 version: 3.1volumes:grafana_data: {}services:influxdb:image: t…

1*1的卷积核如何实现降维/升维?

在众多网络中&#xff0c;1*1的卷积核被引入用来实现输入数据通道数的改变。 举例说明&#xff0c;如果输入数据格式为X*Y*6&#xff0c;X*Y为数据矩阵&#xff0c;6为通道数&#xff0c;如果希望输出数据格式为X*Y*5&#xff0c;使用5个1*1*6的卷积核即可。 其转换过程类似于…