(1.2)、资源处理的默认规则
因为这里分析得到: 假如resourceProperties.isAddMappings()为false下面的业务逻辑都不生效,也就是说默认配置的路径都不会生效。 @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // 静态资源是否全部生效 ⭐ if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); return; } Duration cachePeriod = this.resourceProperties.getCache().getPeriod(); CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl(); //webjars的规则 if (!registry.hasMappingForPattern("/webjars/**")) { customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/") .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } // String staticPathPattern = this.mvcProperties.getStaticPathPattern(); if (!registry.hasMappingForPattern(staticPathPattern)) { customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern) ⭐ .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations())) .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } }
spring: mvc: # 静态资源路径 static-path-pattern: /res/** web: resources: # 静态资源目录位置 static-locations: classpath:/haha 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;
(1.3)、静态页的欢迎规则
HandlerMapping:处理器映射。保存了每一个Handler能处理哪些请求。 @Bean public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) { WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping( new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(), this.mvcProperties.getStaticPathPattern()); welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider)); welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations()); return welcomePageHandlerMapping; } WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Optional<Resource> welcomePage, String staticPathPattern) { ⭐ if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) { //要用欢迎页功能,必须是/** logger.info("Adding welcome page: " + welcomePage.get()); setRootViewName("forward:index.html"); } else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) { // 调用Controller /index logger.info("Adding welcome page template: index"); setRootViewName("index"); } }
欢迎页 静态资源路径如果不是 /** 那么欢迎页将不会生效
3.请求参数处理
(1).请求映射
(1.1)、rest使用与原理
- @xxxMapping;
- Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
- 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
- 现在: /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
- 核心Filter;HiddenHttpMethodFilter
- 用法: 表单method=post,隐藏域 _method=put
- SpringBoot中手动开启
- 扩展:如何把_method 这个名字换成我们自己喜欢的。
- 前端页面
问题:我们后端有四种风格提交Http的风格,而前端只有两种分别为: post、get。如果我们强制把前端的两种提交方式修改成 DELETE风格和 PUT风格,我们发现在后端运行的时候会默认给我们走成 GET风格。这里的原因是我们需要对SpringMVC源码进行查看。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>你好</h1> <form action="/user" method="post"> <button type="submit">Post方式进行跳转</button> </form> <form action="/user" method="get"> <button type="submit">Get方式进行跳转</button> </form> <form action="/user" method="post"> <input name="_method" type="hidden" value="DELETE"> <button type="submit">DELETE方式进行跳转</button> </form> <form action="/user" method="post"> <input name="_method" type="hidden" value="PUT"> <button type="submit">PUT方式进行跳转</button> </form> </body> </html>
业务控制层:
package com.jsxs.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; /** * @Author Jsxs * @Date 2023/7/2 10:54 * @PackageName:com.jsxs.controller * @ClassName: HelloController * @Description: TODO * @Version 1.0 */ @Controller @ResponseBody public class HelloController { @GetMapping("/1.jpg") public String hello(){ return "aaa"; } @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-张三"; } }
在没有配置和遵从SpringMVC源码的情况下,我们是不能实现对多种提交方式进行处理的。
- SpringMvc源码
所有的提交方式源码在这里
@Bean @ConditionalOnMissingBean({HiddenHttpMethodFilter.class}) @ConditionalOnProperty( prefix = "spring.mvc.hiddenmethod.filter", name = {"enabled"}, matchIfMissing = false ) public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() { ⭐ return new OrderedHiddenHttpMethodFilter(); }
点进OrderedHiddenHttpMethodFilter
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.boot.web.servlet.filter; import org.springframework.web.filter.HiddenHttpMethodFilter; public class OrderedHiddenHttpMethodFilter extends ⭐ HiddenHttpMethodFilter implements OrderedFilter { public static final int DEFAULT_ORDER = -10000; private int order = -10000; public OrderedHiddenHttpMethodFilter() { } public int getOrder() { return this.order; } public void setOrder(int order) { this.order = order; } }
点进 HiddenHttpMethodFilter
public class HiddenHttpMethodFilter extends OncePerRequestFilter { private static final List<String> ALLOWED_METHODS; public static final String DEFAULT_METHOD_PARAM = "_method"; private String methodParam = "_method"; 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); } }
得出结论: 假如我们要使用其他的提交方式,首先我们要对 spring.mvc.hiddenmethod.filter.enables
默认的false更改为true。然后我们表单的提交方式要设置为 post 方式进行提交。然后再添加一个隐藏的文本框,将其的name设置成 源码需要的 _methods
并对这个隐藏的文本框进行设置值为 我们想要的提交方式 (DELETE
、PUT
)
Rest原理(表单提交要使用REST的时候)
- 表单提交会带上_method=PUT
- 请求过来被HiddenHttpMethodFilter拦截
- 请求是否正常,并且是POST
- 获取到_method的值。
- 兼容以下请求;PUT.DELETE.PATCH
- 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
- 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
Rest使用客户端工具
- 如PostMan直接发送Put、delete等方式请求,无需Filter。
spring: mvc: # 允许多种方式提交 hiddenmethod: filter: enabled: true web: resources: # 静态资源目录位置 static-locations: classpath:/haha # 静态资源是否映射? add-mappings: true cache: # 设置静态资源的过期时间 period: 10
小结:
@RequestMapping(value = "/user",method = RequestMethod.GET) = GetMapping("/user") @RequestMapping(value = "/user",method = RequestMethod.POST)= PostMapping("/user") ...