一、静态资源:
1.1 静态资源路径:
静态资源在web项目中非常普遍,如图片、css文件、js文件等,springboot项目中,只要静态资源放在以下路径,那么就可以访问到静态资源。
- /static
- /public
- /resources
- /META-INF/resources
1. 静态资源访问示例:
如下,在项目static目录中放入一张图片:
开启项目,使用浏览器访问该静态资源 http://localhost:8081/star.jpg, 如下图所示:
2. 静态资源与Controller中请求同名
如下所示,加入在Controller中有一个和静态资源名相同的请求,那么请求时会如何呢?
@RestController
public class HelloController {
@GetMapping("/star.jpg")
public String Star(){
return "star.jpg";
}
}
请求示例如下:
可以看到,处理的是Controller中的请求。 也就是说,当请求进来之后,首先找Controller看能不能被处理,如果不能处理,则又交给静态资处理器,如果静态资源也找不到,则响应404。
1.2 静态资源访问前缀
spring:
mvc:
static-path-pattern: /res/**
默认情况下,静态资源的访问是无前缀的,但是,也可以在配置文件中配置静态资源的访问前缀,如上文yaml文件配置。
添加访问前缀之后,访问静态资源方式是:当前项目 + static-path-pattern + 静态资源名。这样,我们在请求静态资源时可以使用特定的前缀,避免了与Controller请求的冲突。
1.3 改变默认静态资源路径
如下所示,我们也可以更改静态资源的默认路径:
spring:
mvc:
static-path-pattern: /res/**
resources:
static-locations: [classpath:/mystatic/]
1.4 欢迎页
欢迎页有两种实现方式:
- 静态资源路径下的index.html
在static静态资源路径下,创建index.html文件,然后访问localhost:8081,就会访问到该欢迎页面。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Welcome Wangy</h1>
</body>
</html>
- controller能处理/index 请求
也可以自定义favicon.ico,只需要将favicon.ico 放在静态资源目录下即可。
二、静态资源配置原理
分析:
- SpringBoot在启动时会默认加载xxxAutoConfiguration类(自动配置类),在此处,我们需要关注的是WebMvcAutoConfiguration类
- 首先确定该配置类生效;在该配置类中,为容器配置了WebMvcAutoConfiguration组件
@Configuration(
proxyBeanMethods = false
)
@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
@EnableConfigurationProperties({WebMvcProperties.class, WebProperties.class})
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {}
该组件中,将配置文件的相关属性和xxx进行了绑定,WebMvcProperties==spring.mvc、ResourceProperties==spring.resources
该组件中规定了资源处理的默认规则,如下所示:
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
registration.addResourceLocations(new Resource[]{resource});
}
});
}
}
分析上述代码可以发现,首先,我们可以禁用所有的静态资源规则:
spring:
# mvc:
# static-path-pattern: /res/**
resources:
add-mappings: false 禁用所有静态资源规则
其次,我们也可以看到静态资源的映射路径:
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
/**
* Locations of static resources. Defaults to classpath:[/META-INF/resources/,
* /resources/, /static/, /public/].
*/
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
三、请求参数处理
Rest风格支持!如下所示:
@RequestMapping(value = "/user",method = RequestMethod.GET)
public String getUser(){
return "GET-张三";
}
@RequestMapping(value = "/user",method = RequestMethod.POST)
public String saveUser(){
return "POST-张三";
}
@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String putUser(){
return "PUT-张三";
}
@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){
return "DELETE-张三";
}
但是,此处有一个限制,如果我们使用客户端工具,如PostMan发出Put、delete等方式请求,那么可以直接处理。但是,当我们使用表单提交时,会发现,是没有除了post及get方式以外的请求方法的!此时:
- 我们首先应该在表单提交上带_method=PUT(或其他方法),并且保证其提交方法为Post
<form action="/user" method="post">
<input name="_method" type="hidden" value="PUT">
<input value="REST_PUT 提交" type="submit" >
</form>
- 而后端,我们应该配核心Filter;HiddenHttpMethodFilter,让其拦截所有的请求:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter((ServletRequest)requestToUse, response);
}
分析以上代码,
1. 首先请求来临后,会判断其是否为Post请求,若不是,则放行,若是,则进行拦截。
2. 拦截之后,获取到请求携带的参数_method的值,判断其是否属于兼容的以下请求,即PUT、DELETE、PATCH;
3. 若是,则对原生的request,使用包装模式requesWrapper重写了getMethod方法,将_method值作为其返回值。
4. 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的,即返回的是_method的值。
该过滤器需要在springboot配置文件中进行手动开启,如下所示:
spring:
mvc:
hiddenmethod:
filter:
enabled: true #开启页面表单的Rest功能