Spring Boot Web 开发
- 导入静态资源
- 定制首页
- jsp 模板引擎
- 装配扩展 SpringMVC
- 增删改查
- 拦截器
- 国际化(中英文切换)
导入静态资源
在图中的五个路径下的静态资源都可以被读取
/**,classpath:/WEB-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
classpath:/resources/ 下面的资源读取优先级最高,然后是 classpath:/static/,最后是 classpath:/public/
定制首页
private Resource getWelcomePage() {
for (String location : this.resourceProperties.getStaticLocations()) {
Resource indexHtml = getIndexHtml(location);
if (indexHtml != null) {
return indexHtml;
}
}
ServletContext servletContext = getServletContext();
if (servletContext != null) {
return getIndexHtml(new ServletContextResource(servletContext, SERVLET_LOCATION));
}
return null;
}
private Resource getIndexHtml(String location) {
return getIndexHtml(this.resourceLoader.getResource(location));
}
private Resource getIndexHtml(Resource location) {
try {
Resource resource = location.createRelative("index.html");
if (resource.exists() && (resource.getURL() != null)) {
return resource;
}
}
catch (Exception ex) {
}
return null;
}
返回静态资源目录下的 index.html 文件作为首页
Thymeleaf 模板引擎
- Spring Boot 使用的是嵌入式的 Tomcat,默认不支持 JSP
- 纯静态的页面会给开发带来非常大的麻烦,Spring Boot 推荐使用模板引擎
- JSP 就是一个模板引擎,Spring Boot 推荐使用 Thymeleaf 模板引擎
- 所有的页面的静态资源都需要使用 Thymeleaf 接管(@{})
准备工作
- 开启 Thymeleaf 启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
- 所有的 Thymeleaf 模板引擎都写在 templates 下
- 默认的编码和前后缀
- 在 html 文件中加入 Thymeleaf 约束
<html xmlns:th="http://www.thymeleaf.org">
传值
- 所有的 html 元素都可以被 Thymeleaf 通过
th:元素名
替换接管
@Controller
public class IndexController {
@RequestMapping("/test")
public String index(Model model){
model.addAttribute("msg","测试成功");
return "test";
}
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:text="${msg}"></div>
</body>
</html>
Thymeleaf 语法
th:text 和 th:utext
- th:text:获取参数的值
- th:utext:获取参数的值时,如果值中存在 html 标签,会解析 html,显示相应的效果
th:each 遍历
@Controller
public class IndexController {
@RequestMapping("/test")
public String index(Model model){
model.addAttribute("lists", Arrays.asList("147","258"));
return "test";
}
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:each="list:${lists}" th:text="${list}"></div>
<!--或者<div th:each="list:${lists}">[[ ${list} ]]</div>-->
</body>
</html>
数据格式 | 表达式 |
---|---|
普通变量(Variable Expressions) | ${…} |
国际化消息(Message Expressions) | #{…} |
URL(Link URL Expressions) | @{…} |
片段表达式(Fragment Expressions) | ~{…} |
文本(Text literals) | ‘你好’ |
数字(Number literals) | 2 |
布尔值(Boolean literals) | true,false |
空值(Null literals) | null |
标志符(Literals tokens) | one,sometext,main… |
文本操作—字符串连接(String concatenation) | + |
数学运算 | +,-,*,/,% |
布尔表达式 | and,or,!,not |
比较运算 | >,<,>=,<=,==,!= |
三元条件运算符 | (if)?(then),(if)?(then):(else) |
公共页面的提取
- 在公共页面标签上加入
th:fragment="自定义组件名"
- 在需要使用公共页面的地方插入
<div th:replace="~{公共页面位置::自定义组件名}"></div>
- 通过公共页面的提取,可以实现页面的复用,简化了代码,提高了效率
扩展装配 SpringMVC
-
扩展 SpringMVC:
新建一个类,加 @Configuration 注解让这个类变成一个配置类,并让实现 WebMvcConfigurer 接口
-
在 springboot 中有非常多的 xxxx Configuration 帮助我们进行扩展配置
@Configurationpublic class MyMvcConfig implements WebMvcConfigurer {}
扩展/自定义视图解析器
- 自定义一个类实现 ViewResolver 接口成为视图解析器
public static class MyViewResolver implements ViewResolver{
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
return null;
}
}
- 方法加 @Bean 产生一个视图解析器对象并交给 Spring 的 IOC 容器管理,帮我们自动装配
@Bean
public ViewResolver myViewResolver(){
return new MyViewResolver();
}
国际化
- 也可以看做是一种 SpringMVC 扩展装配
- 确保项目编码格式为 UTF-8
- 下载该插件开启可视化配置
- 创建配置文件
- 编写配置文件
- 在 springboot 核心配置文件中设置国际化配置文件的位置
#配置文件的真实位置
spring:
messages:
basename: i18n.login
- 将值写入 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" action="dashboard.html">
<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}">Please sign in</h1>
<label class="sr-only">Username</label>
<input type="text" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
<label class="sr-only">Password</label>
<input type="password" class="form-control" 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">[[#{login.btn}]]</button>
<p class="mt-5 mb-3 text-muted">© 2021-2022</p>
<a class="btn btn-sm" th:href="@{index(language='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{index(language='en_US')}">English</a>
</form>
</body>
</html>
- 自定义国际化视图类组件
public class MyLocaleResolver implements LocaleResolver {
//解析请求
@Override
public Locale resolveLocale(HttpServletRequest request) {
//获取请求中的语言参数
String language = request.getParameter("language");
Locale locale = Locale.getDefault();
if (!StringUtils.isEmpty(language)){
String[] split = language.split("_");
locale = new Locale(split[0], split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
- 将自定义组件在配置类中装配到 Spring 容器中
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
}
拦截器
登录页面问题
- 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}">
<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}">Please sign in</h1>
<p style="color:red" th:text="${msg}"></p>
<label class="sr-only">Username</label>
<input type="text" name="username" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
<label class="sr-only">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"> [[#{login.remember}]]
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">[[#{login.btn}]]</button>
<p class="mt-5 mb-3 text-muted">© 2021-2022</p>
<a class="btn btn-sm" th:href="@{index(language='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{index(language='en_US')}">English</a>
</form>
</body>
</html>
- Controller
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model){
if (!StringUtils.isEmpty(username)&&password.equals("123456")){
return "redirect:/main";
}else {
model.addAttribute("msg","用户名或密码错误!");
return "index";
}
}
}
- WebMvcConfigurer
- 编写试图跳转,掩盖 url 中的参数
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index").setViewName("index");
registry.addViewController("/main").setViewName("dashboard");
}
}
- Controller
- 重定向到自己编写的视图跳转中
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model){
if (!StringUtils.isEmpty(username)&&password.equals("123456")){
return "redirect:/main";
}else {
model.addAttribute("msg","用户名或密码错误!");
return "index";
}
}
}
存在问题:设置重定向跳转后,不需要登录,输入 url 可以直接跳转进页面,为了解决该问题,需要使用拦截器
拦截器
- 编写一个类实现 HandlerInterceptor 称为拦截器
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){
request.setAttribute("msg","您没有权限,请先登录");
request.getRequestDispatcher("/index").forward(request,response);
return false;
}else {
return true;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
- 在配置类添加拦截器
- 拦截除了 /index,/,/user/login,/static/** 的所有请求
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index").setViewName("index");
registry.addViewController("/main").setViewName("dashboard");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandlerInterceptor())
.addPathPatterns("/**").
excludePathPatterns("/index","/","/user/login","/static/**");
}
}
- 测试拦截器