/static
(or /public
or /resources
or /META-INF/resources
但是需要注意: 请求进来,先去找Controller
看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面。
修改默认的资源访问目录:
resources:
# 配置是数组的形式,可以定义多个目录
static-locations: [classpath:/test/]
spring:
mvc:
static-path-pattern: /res/**
当前项目 + static-path-pattern + 静态资源名
例如:http://localhost:8080/res/xxx.jpg
将Favicon放到静态资源目录下即可。
静态资源路径下 index.html
通过controller进行设置
根据源码分析直接得出结论:
Rest原理(表单提交要使用REST的时候)
表单提交会带上 _method=PUT
请求过来被 HiddenHttpMethodFilter
拦截
request(post)
,包装模式requesWrapper
重写了getMethod
方法,返回的是传入的值。wrapper
。以后的方法调用getMethod是调用requesWrapper
的补充:
Rest使用客户端工具,如PostMan直接发送Put、delete等方式请求,无需Filter。
实际上执行功能的类是DispatcherServlet
中的doDispatch
方法实现功能。
在doDispatch
方法中调用了getHandler
方法获取应该处理请求的Handler或Controller:
通过循环依次遍历:
@PathVariable
用于映射URL绑定的占位符,使用@RequestMapping
URI template 样式映射时,例如:xxx/{paramId}
,可通过 @Pathvariable注解绑定它传过来的值到方法的参数上。
@GetMapping("/getUser/{userId}")
public String getUserId(@PathVariable("userId") Integer userId) {
return "userId = " + userId;
}
@RequestHeader
是获取请求头中的数据,通过指定参数 value
的值来获取请求头中指定的参数值。
@GetMapping("/getHeader")
public String getHeader(@RequestHeader("User-Agent") String userAgent) {
return "User-Agent: " + userAgent;
}
@RequestParam
获取请求参数
@GetMapping("/getParam")
public String getParam(@RequestParam("userId") Integer userId) {
return "userId = " + userId;
}
获取Cookie值
@GetMapping("/getCookieValue")
public String getCookieValue(@CookieValue("_ga") String _ga, @CookieValue Cookie cookie) {
return "success";
}
获取request域
属性
@Controller
public class RequestController {
@GetMapping("/goto")
public String goToPage(HttpServletRequest request){
request.setAttribute("msg","成功了...");
request.setAttribute("code",200);
return "forward:/success"; //转发到 /success请求
}
@GetMapping("/params")
public String testParam(Map<String,Object> map,
Model model,
HttpServletRequest request,
HttpServletResponse response){
map.put("hello","world666");
model.addAttribute("world","hello666");
request.setAttribute("message","HelloWorld");
Cookie cookie = new Cookie("c1","v1");
response.addCookie(cookie);
return "forward:/success";
}
///@RequestAttribute在这个方法被使用
@ResponseBody
@GetMapping("/success")
public Map success(@RequestAttribute(value = "msg",required = false) String msg,
@RequestAttribute(value = "code",required = false)Integer code,
HttpServletRequest request){
Object msg1 = request.getAttribute("msg");
Map<String,Object> map = new HashMap<>();
Object hello = request.getAttribute("hello");
Object world = request.getAttribute("world");
Object message = request.getAttribute("message");
map.put("reqMethod_msg",msg1);
map.put("annotation_msg",msg);
map.put("hello",hello);
map.put("world",world);
map.put("message",message);
return map;
}
}
获取请求体,注意是POST请求。
@PostMapping("/getRequestBody")
public String getRequestBody(@RequestBody String body) {
return body;
}
在视图解析层面:SpringBoot默认不支持 JSP,需要引入第三方模板引擎技术实现页面渲染。
视图的处理方式有三种:
视图解析的流程:
1、目标方法处理的过程中,所有数据都会被放在 ModelAndViewContainer
里面。包括数据和视图地址。
2、方法的参数是一个自定义类型对象(从请求参数中确定的),把他重新放在ModelAndViewContainer
3、任何目标方法执行完成以后都会返回 ModelAndView(数据和视图地址)。
4、processDispatchResult 处理派发结果(页面改如何响应)
1、render(mv, request, response); 进行页面渲染逻辑
response.sendRedirect(encodedURL);
Thymeleaf is a modern server-side Java template engine for both web and standalone environments.
Thymeleaf是用于Web和独立环境的现代服务器端Java模板引擎。
— 官方文档
① 表达式
表达式名字 | 语法 | 用途 |
---|---|---|
变量取值 | ${...} |
获取请求域、session域、对象等值 |
选择变量 | *{...} |
获取上下文对象值 |
消息 | #{...} |
获取国际化等值 |
链接 | @{...} |
生成链接 |
片段表达式 | ~{...} |
jsp:include 作用,引入公共页面片段 |
② 字面量
③ 文本操作
+
|The name is ${name}|
<div th:class="'content'">...div>
<span th:text="|Welcome to our application, ${user.name}!|">
//Which is equivalent to:
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
<span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">
④ 数学运算
⑤ 布尔运算
⑥ 比较运算
⑦ 条件运算符
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
⑧ 设置属性值-th:attr
<form action="subscribe.html" th:attr="action=@{/subscribe}">
<fieldset>
<input type="text" name="email" />
<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
fieldset>
form>
<img src="../images/xxx.png" th:attr="src=@{/images/xxx.png},title=#{logo},alt=#{logo}" />
th:xxxx
<img src="../images/xxx.png" th:attr="src=@{/images/xxx.png},title=#{logo},alt=#{logo}" />
⑨ 迭代
使用th:each
<tr th:each="prod : ${prods}">
<td th:text="${prod.name}">Onionstd>
<td th:text="${prod.price}">2.41td>
<td th:text="${prod.inStock}? #{true} : #{false}">yestd>
tr>
<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
<td th:text="${prod.name}">Onionstd>
<td th:text="${prod.price}">2.41td>
<td th:text="${prod.inStock}? #{true} : #{false}">yestd>
tr>
⑩ 条件运算
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">viewa>
<div th:switch="${user.role}">
<p th:case="'admin'">User is an administratorp>
<p th:case="#{roles.manager}">User is a managerp>
<p th:case="*">User is some other thingp>
div>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
Thymeleaf 的自动配置类:ThymeleafAutoConfiguration
:
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ThymeleafProperties.class)
@ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class })
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })
public class ThymeleafAutoConfiguration { }
自动配好的策略:
首先要引入名称空间:
<html lang="en" xmlns:th="http://www.thymeleaf.org">
实现拦截请求功能的方式有两种:
HandlerInterceptor
实现拦截器。实现拦截器的大致流程:
HandlerInterceptor
接口假设一个业务场景:
一个后台管理系统,第一次进入时需要登录,也就是说没有登录却访问主页面的请求会被拦截住。实现这个功能的方式就是通过拦截器来拦截没有登录的请求。
实现HandlerInterceptor
接口重写其中的方法:
preHandle
:方法执行之前拦截postHandle
:方法执行之拦截afterCompletion
:页面渲染完成之后执行注意: 要在自定义的拦截器实现类上标记@Component
注解,将其添加到容器中。
package com.jc.admin.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@Slf4j
@Component
public class loginInterceptor implements HandlerInterceptor {
/**
* 方法执行之前拦截
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
if (loginUser != null) {
log.info("用户已经登录,执行放行操作");
return true;
}
// 用户没有登录,将请求转发到登录页
session.setAttribute("msg", "请先登录!");
// 获取请求调度程序,然后进行请求转发
request.getRequestDispatcher("/").forward(request, response);
return false;
}
/**
* 方法执行之后拦截
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("后置处理方法执行");
}
/**
* 页面渲染完成之后执行
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("视图渲染后方法执行");
}
}
注册拦截器这一步需要在配置类中进行,同时配置类需要实现addInterceptors
方法。
package com.jc.admin.config;
import com.jc.admin.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Autowired
LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") // 添加要拦截的内容
.excludePathPatterns("/","/login", "/css/**", "/fonts/**", "/images/**", "/js/**");// 排除要拦截的内容
}
}
HandlerExecutionChain
(可以处理请求的handler以及handler的所有 拦截器)preHandle()
方法。
preHandle()
返回为true
。则执行下一个拦截器的preHandle()
false
。直接倒序执行所有已经执行了的拦截器的 afterCompletion();
。false
,直接跳出不执行目标方法。true
,才执行目标方法。postHandle()
方法。afterCompletion()
。afterCompletion()
。在Controller
层的方法参数中放置MultipartFile
类型的属性。
<form role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="exampleInputEmail1">邮箱label>
<input type="email" name="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email">
div>
<div class="form-group">
<label for="exampleInputPassword1">名字label>
<input type="text" name="username" class="form-control" id="exampleInputPassword1" placeholder="Password">
div>
<div class="form-group">
<label for="exampleInputFile">头像label>
<input type="file" name="headerImg" id="exampleInputFile">
div>
<div class="form-group">
<label for="exampleInputFile">生活照label>
<input type="file" name="photos" multiple>
div>
<div class="checkbox">
<label>
<input type="checkbox"> Check me out
label>
div>
<button type="submit" class="btn btn-primary">提交button>
form>
@Controller
public class FormController {
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("username") String username,
@RequestPart("headerImg") MultipartFile headerImg,
@RequestPart("photos") MultipartFile[] photos) throws IOException {
if (!headerImg.isEmpty()) {
// 保存文件操作:保存到本地或者OSS
String filename = headerImg.getOriginalFilename();
headerImg.transferTo(new File("E:\\springboot-study\\uploadfile" + filename));
}
if (photos.length > 0) {
for (MultipartFile photo : photos) {
String filename = photo.getOriginalFilename();
photo.transferTo(new File("E:\\springboot-study\\uploadfile" + filename));
}
}
return "main";
}
}
# 上传文件最大大小
spring.servlet.multipart.max-file-size=10MB
# 一次请求中上传的所有文件最大大小
spring.servlet.multipart.max-request-size=100MB