本文共 10853 字,大约阅读时间需要 36 分钟。
目录
第七章 Web开发
7.3 请求参数处理
7.3.1 请求映射
7.3.1.1 Rest使用与原理
在一个方法上加注解@RequestMapping,来声明这个方法能处理什么请求,这个声明过程就是请求映射。
- Rest风格支持:使用Http请求方式的动词来表示对资源的操作
- 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
- 现在:/user(路径都是一样的) GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
- Rest风格实现的核心Filter:HiddenHttpMethodFilter
-
WebMvcAutoConfiguration类中有一个方法hiddenHttpMethodFilter()
-
OrderedHiddenHttpMethodFilter要继承HiddenHttpMethodFilter
-
用法:表单method=post,使用post请求方式。隐藏域_method=put
-
SpringBoot中手动开启
-
@Bean@ConditionalOnMissingBean({HiddenHttpMethodFilter.class})@ConditionalOnProperty( prefix = "spring.mvc.hiddenmethod.filter", name = {"enabled"})public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() { return new OrderedHiddenHttpMethodFilter();}
public class OrderedHiddenHttpMethodFilter extends HiddenHttpMethodFilter implements OrderedFilter {}
public class HiddenHttpMethodFilter extends OncePerRequestFilter { private static final ListALLOWED_METHODS; public static final String DEFAULT_METHOD_PARAM = "_method"; private String methodParam = "_method";//后续可以在自定义的组件中修改这个参数实现自定义名字 //允许put、delete、patch static { ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name())); } protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { HttpServletRequest requestToUse = request; //需要是post请求方式,参数是_method if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) { //获取_method属性的值 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); }}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper { private final String method; public HttpMethodRequestWrapper(HttpServletRequest request, String method) { super(request); this.method = method;//保存新的请求方式,即从_method中得到的值 } public String getMethod() { return this.method;//然后把这个新的请求方式返回 }}
具体实现:
spring: mvc: hiddenmethod: filter: enabled: true #手动开启该功能
- Rest原理(表单提交使用Rest的时候):总体来看就是用一个过滤器把原生请求的getMethod方法重写了,换成了新的请求方式。
- 表单提交会带上_method=PUT
- 请求过来被HiddenHttpMethodFilter拦截
- 请求是否正常,并且是POST
-
获取到_method的值
-
兼容以下请求;PUT.DELETE.PATCH
- 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是_method的值。
- 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
-
- 请求是否正常,并且是POST
- Rest如果使用客户端工具
- 如PostMan可以直接发送put、delete等请求方式,就不需要这个Filter了。此时请求一开始就不是post了,也就没有进入后续方法的条件。
- SpringBoot中因此也产生了新注解,但其实底层实现的就是@RequestMapping
- @GetMapping("/user")
- @PostMapping("/user")
- @PutMapping("/user")
- @DeleteMapping("/user")
- 如何修改_method,改成自定义的
- 底层实现是如果没有手动写HiddenHttpMethodFilter.class这个组件,那么默认就会自动创建一个这个组件,默认判断的就是_method
- 可以自己定义一个HiddenHttpMethodFilter.class。
7.3.1.2 请求映射原理
底层还是SpringMVC,所有请求都会到DispatcherServlet。
SpringMVC功能分析都从org.springframework.web.servlet.DispatcherServlet->doDispatch()
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { //传入原生的request、response HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false;//是不是一个文件上传请求 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);//异步管理器 try { try { ModelAndView mv = null; Object dispatchException = null; try { //checkMultipart检查是否是文件上传请求 processedRequest = this.checkMultipart(request); //如果是文件上传请求,进行一个转化 multipartRequestParsed = processedRequest != request; //找到当前请求使用哪个Handler(Controller的方法)处理 mappedHandler = this.getHandler(processedRequest);
getHandler方法中,handlerMapping:处理器映射。/xxx->>xxxx,即各种映射规则。
@Nullableprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { Iterator var2 = this.handlerMappings.iterator(); while(var2.hasNext()) { HandlerMapping mapping = (HandlerMapping)var2.next(); HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null;}
RequestMappingHandlerMapping:保存了所有@RequestMapping注解和handler的映射
规则。
比如请求调用getUser()过程,先按照写的"/user"找到四种可能的(get、post、delete、put),
再从这四种中找到最匹配的那个。注意因此要求对同一种请求,只能有一个最匹配的处理方
式。
应用一启动,SpringMVC自动扫描所有的controller并解析注解,把这些注解信息全部保存到handlerMapppings中。然后挨个找所有得请求映射,看哪个可以处理请求。
- 所有的请求映射都在handlerMappings中
- SpringBoot自动配置欢迎页的WelcomePageHandlerMapping 。访问"/"能访问到index.html
- SpringBoot自动配置了默认的RequestMappingHandlerMapping
-
请求进来,挨个尝试所有的HandlerMapping看是否有请求信息
- 如果有就找到这个请求对应的handler
- 如果没有就比较下一个 HandlerMapping
- 我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping,自定义 HandlerMapping
7.3.2 普通参数与基本注解
7.3.2.1 注解
@PathVariable:接受请求路径中占位符的值。可以将URL占位符参数{xxx}绑定到处理器类的方法形参中@PathVariable(“xxx“)。
①可以一个个取路径中的值
②也可以把路径中的所有参数及其值放在一个Map<String,String>中
@RequestHeader:获取请求头中的数据,通过指定参数value的值来获取请求头中指定的参数值。
①可以一个个取请求头中的参数
②也可以直接把请求头中的所有参数放在一个Map<String,String>中
@ModelAttribute:运用在参数上,会将客户端传递过来的参数按名称注入到指定对象中,并且会将这个对象自动加入ModelMap中,便于View层使用;被@ModelAttribute注释的方法会在此controller的每个方法执行前被执行 ,如果有返回值,则自动将该返回值加入到ModelMap中。
@RequestParam:将请求参数绑定到控制器的方法参数上(是springmvc中接收普通参数的注解)
①可以一个个取请求参数
②也可以直接把所有请求参数放在一个Map<String,String>中
@MatrixVariable:注解拓展了URl请求地址的功能。使用@MatrixVariable注解时多个变量可以使用;(分号)分隔。矩阵变量绑定在路径变量中。SpringBoot默认关闭这个功能,要自己手动开启。
原理:WebMvcAutoConfiguration类中的静态类WebMvcAutoConfigurationAdapter中有一个
方法configurePathMatch(),再查看类UrlPathHelper,里面有一个属性
removeSemicolonContent,当设置为true时会把URL中";"后面的去掉,而默认为true。需要
自定义规则:@Configuration + 实现WebMvcConfigurer接口来来自定义规则。
public void configurePathMatch(PathMatchConfigurer configurer) { if (this.mvcProperties.getPathmatch().getMatchingStrategy() == MatchingStrategy.PATH_PATTERN_PARSER) { configurer.setPatternParser(WebMvcAutoConfiguration.pathPatternParser); } configurer.setUseSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseSuffixPattern()); configurer.setUseRegisteredSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseRegisteredSuffixPattern()); this.dispatcherServletPath.ifAvailable((dispatcherPath) -> { String servletUrlMapping = dispatcherPath.getServletUrlMapping(); if (servletUrlMapping.equals("/") && this.singleDispatcherServlet()) { UrlPathHelper urlPathHelper = new UrlPathHelper(); urlPathHelper.setAlwaysUseFullPath(true); configurer.setUrlPathHelper(urlPathHelper); } });}
@Configuration( proxyBeanMethods = false )@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})@EnableConfigurationProperties({WebMvcProperties.class, WebProperties.class})@Order(0)public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {}public interface WebMvcConfigurer { default void configurePathMatch(PathMatchConfigurer configurer) { }}
第一种实现WebMvcConfigurer接口方式:直接让配置类实现该接口,然后重写方法。
第二种实现WebMvcConfigurer接口方式:直接在容器中放一个这个类型的组件。
@CookieValue:用来获取Cookie中的值。
@RequestBody:用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的),所以只能发送POST请求。GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST方式进行提交。@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。注:一个请求,只有一个RequestBody;一个请求,可以有多个RequestParam。
@RequestAttribute:获取request域属性,即获取HTTP的请求(request)对象属性值,用来传递给控制器的参数。
@ResponseBody:作用是将java对象转为json格式的数据。将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区。
比如有以下请求:
get请求:
post请求:
@RestControllerpublic class ParameterTestController { // car/2/owner/zhangsan @GetMapping("/car/{id}/owner/{username}") public MapgetCar(@PathVariable("id") Integer id, @PathVariable("username") String name, @PathVariable Map pv, @RequestHeader("User-Agent") String userAgent, @RequestHeader Map header, @RequestParam("age") Integer age, @RequestParam("inters") List inters, @RequestParam Map params, @CookieValue("_ga") String _ga, @CookieValue("_ga") Cookie cookie){ Map map = new HashMap<>();// map.put("id",id);// map.put("name",name);// map.put("pv",pv);// map.put("userAgent",userAgent);// map.put("headers",header); map.put("age",age); map.put("inters",inters); map.put("params",params); map.put("_ga",_ga); System.out.println(cookie.getName()+"===>"+cookie.getValue()); return map; } @PostMapping("/save") public Map postMethod(@RequestBody String content){ Map map = new HashMap<>(); map.put("content",content); return map; } //1、语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd //2、SpringBoot默认是禁用了矩阵变量的功能 // 手动开启:原理。对于路径的处理。UrlPathHelper进行解析。 // removeSemicolonContent(移除分号内容)支持矩阵变量的 //3、矩阵变量必须有url路径变量才能被解析 @GetMapping("/cars/{path}") public Map carsSell(@MatrixVariable("low") Integer low, @MatrixVariable("brand") List brand, @PathVariable("path") String path){ Map map = new HashMap<>(); map.put("low",low); map.put("brand",brand); map.put("path",path); return map; } // url:/boss/1;age=20/2;age=10 @GetMapping("/boss/{bossId}/{empId}") public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge, @MatrixVariable(value = "age",pathVar = "empId") Integer empAge){ Map map = new HashMap<>(); map.put("bossAge",bossAge); map.put("empAge",empAge); return map; }}
@Controllerpublic class RequestController { @GetMapping("/goto") public String gotoPage(HttpServletRequest request) { request.setAttribute("msg","成功了"); //向请求作用域中加入参数 request.setAttribute("code",200); return "forward:/success"; //转发到/success } @ResponseBody @GetMapping("/success") public Map success(@RequestAttriubute("msg") String msg, @RequestAttriubute("code") Integer code, HttpServletRequest request) { Object msg1 = request.getAttribute("msg"); Mapmap = new HashMap<>; map.put("reqMethod_msg",msg1); map.put("annnotation_masg",msg); return map; }}
PS:根据尚硅谷视频整理,如有侵权,联系删除。
转载地址:https://blog.csdn.net/xxyneymar/article/details/122562779 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!