上一级页面:ssm-spring-boot速成学习索引
前言
26、请求处理-【源码分析】-Rest映射及源码解析
请求映射
@xxxMapping;
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
Rest风格支持
Rest风格支持(使用HTTP 请求方式动词来表示对资源的操作)
以前:
/getUser 获取用户
/deleteUser 删除用户
/editUser 修改用户
/saveUser保存用户
现在: /user
GET-获取用户
DELETE-删除用户
PUT-修改用户
POST-保存用户
核心Filter;HiddenHttpMethodFilter
Rest用法
用法
开启页面表单的Rest功能
页面 form的属性method=post,隐藏域 _method=put、delete等(如果直接get或post,无需隐藏域)
编写请求映射
application.yaml中添加以下配置
1 2 3 4 5 spring: mvc: hiddenmethod: filter: enabled: true
将以下内容添加至index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <h1 > 测试RESTFUL</h1 > <form action ="/user" method ="get" > <input value ="REST-GET提交" type ="submit" /> </form > <form action ="/user" method ="post" > <input value ="REST-POST提交" type ="submit" /> </form > <form action ="/user" method ="post" > <input name ="_method" type ="hidden" value ="DELETE" /> <input value ="REST-DELETE 提交" type ="submit" /> </form > <form action ="/user" method ="post" > <input name ="_method" type ="hidden" value ="PUT" /> <input value ="REST-PUT提交" type ="submit" /> </form >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @GetMapping("/user") public String getUser () { return "GET-张三" ; } @PostMapping("/user") public String saveUser () { return "POST-张三" ; } @PutMapping("/user") public String putUser () { return "PUT-张三" ; } @DeleteMapping("/user") public String deleteUser () { return "DELETE-张三" ; }
Spring使用Rest的原理
这里用到了包装类HttpMethodRequestWrapper
Rest原理(表单提交要使用REST的时候)
表单提交会带上_method=PUT
请求过来被HiddenHttpMethodFilter
拦截
请求是否正常,并且是POST
获取到_method
的值。
兼容以下请求;PUT .DELETE .PATCH
原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public class HiddenHttpMethodFilter extends OncePerRequestFilter { private static final List<String> ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name())); public static final String DEFAULT_METHOD_PARAM = "_method" ; private String methodParam = DEFAULT_METHOD_PARAM; public void setMethodParam (String methodParam) { Assert.hasText(methodParam, "'methodParam' must not be empty" ); this .methodParam = methodParam; } @Override protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { HttpServletRequest requestToUse = request; if ("POST" .equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null ) { String paramValue = request.getParameter(this .methodParam); if (StringUtils.hasLength(paramValue)) { String method = paramValue.toUpperCase(Locale.ENGLISH); if (ALLOWED_METHODS.contains(method)) { requestToUse = new HttpMethodRequestWrapper (request, method); } } } filterChain.doFilter(requestToUse, response); } private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper { private final String method; public HttpMethodRequestWrapper (HttpServletRequest request, String method) { super (request); this .method = method; } @Override public String getMethod () { return this .method; } } }
RestApi测试工具ApiPost、PostMan(建议后端程序员学习使用)
Rest使用客户端工具。
如PostMan可直接发送put、delete等方式请求。
27、请求处理-【源码分析】-怎么改变默认的_method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration { ... @Bean @ConditionalOnMissingBean(HiddenHttpMethodFilter.class) @ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false) public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter () { return new OrderedHiddenHttpMethodFilter (); } ... }
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
意味着在没有HiddenHttpMethodFilter
时,才执行hiddenHttpMethodFilter()
。
因此,我们可以自定义filter,改变默认的_method
。例如:
1 2 3 4 5 6 7 8 9 10 @Configuration(proxyBeanMethods = false) public class WebConfig { @Bean public HiddenHttpMethodFilter hiddenHttpMethodFilter () { HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter (); methodFilter.setMethodParam("_m" ); return methodFilter; } }
将_method
改成_m
。
1 2 3 4 <form action ="/user" method ="post" > <input name ="_m" type ="hidden" value ="DELETE" /> <input value ="REST-DELETE 提交" type ="submit" /> </form >
28、请求处理-【源码分析】-请求映射原理
HttpServler DoGet -> FrameworkServlet processRequest
FrameworkServlet processRequest -> 调用FrameworkServlet doService(抽象方法)
FrameworkServlet doService(抽象方法) -> 由DispatcherServlet doService进行具体实现
DispatcherServlet doService -> 调用 DispatcherServlet doDispatch
总结:SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet
-> doDispatch()
开始,以后源码分析经常从这开始,断点也打在这
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 protected void doDispatch (HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null ; boolean multipartRequestParsed = false ; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null ; Exception dispatchException = null ; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); mappedHandler = getHandler(processedRequest); }
getHandler()
方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 @Nullable protected HandlerExecutionChain getHandler (HttpServletRequest request) throws Exception { if (this .handlerMappings != null ) { for (HandlerMapping mapping : this .handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null ) { return handler; } } } return null ; }
this.handlerMappings
在Debug模式下展现的内容:
其中,RequestMappingHandlerMapping
保存了所有@RequestMapping
和handler
的映射规则。
在SpringBoot3.0+,这个方法被更改了
但是和2.0+区别不大。
所有的请求映射都在HandlerMapping中:
SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /
能访问到index.html
;
SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
如果有就找到这个请求对应的handler
如果没有就是下一个 HandlerMapping
我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping 。自定义 HandlerMapping
IDEA快捷键
IDEA快捷键:
Ctrl + Alt + U : 以UML的类图展现类有哪些继承类,派生类以及实现哪些接口。
Crtl + Alt + Shift + U : 同上,区别在于上条快捷键结果在新页展现,而本条快捷键结果在弹窗展现。
Ctrl + H : 以树形方式展现类层次结构图。
SpringBootWeb开发-2-1
spring-boot-web-开发-2-1-请求处理和api测试
参考、引用、致谢