SpringBoot请求参数处理 常见注解

请求参数处理

SpringBoot在底层使用SpringMVC完成web请求处理。

⭐️请求映射与Rest风格

形如@xxxMapping这样的注解用来处理请求映射,最常见的如@RequestMapping

SpringBoot现在支持Rest风格,Rest风格使用HTTP请求方式动词来表示对资源的操作,例如:

获取用户删除用户修改用户保存用户
传统风格getUserdeletUserupdateUsersaveUser
Rest风格GETDELETEPUTPOST

传统命名风格命名繁琐,对每一种行为都要单独命名一次,后期行为功能很多时极其繁琐,推荐使用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-张三";
}

SpringMVC中使用Rest风格关键是要配置一个核心FilterHiddenHttpMethodFilter,否则仍然只能处理get和post请求。SpringBoot中已经自动配置,但需要在表单中进行如下配置:

<!-- 测试REST风格;-->
<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"> <!--其他请求方法类型也用post-->
    <input name="_method" type="hidden" value="delete"/> <!--关键在这里-->
    <input name="_m" 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>

通过对WebMvcAutoConfiguration源码的分析:

@Bean
@ConditionalOnMissingBean({HiddenHttpMethodFilter.class}) //没有该Bean时
@ConditionalOnProperty( //主配置文件属性定义
    prefix = "spring.mvc.hiddenmethod.filter",
    name = {"enabled"},
    matchIfMissing = false //默认为false
)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
    return new OrderedHiddenHttpMethodFilter();
}

可知同时要在主配置文件中手动开启spring.mvc.hiddenmethod.filter=true

💡本质上仍然只有get和post请求,只不过配一个过滤器,过滤器根据该表单中隐藏参数的名字去找对应的Rest请求方法名。

表单提交Rest风格请求方法原理

表单提交必须带_method的值,例如_method=PUT。表单请求发送到服务器被HiddenHttpMethodFilter拦截。判断请求是否是POST,服务器获取_method的值。SpringBoot内部重写了getMethod方法修改方法名并返回。除GET和POST外还兼容PUT, DELETE, PATCH方法名。

⚠️该原理仅适用于表单提交请求,因为表单仅有put和get。

@GetMapping

可以使用@GetMapping("/user")替换@RequestMapping("/user", method = RequestMethod.GET),类似的Spring还提供@DeleteMapping, @PutMapping, @PostMapping,效果一样。

修改默认_method名字

容器中的HiddenHttpMethodFiltermethodParam=_methond,想要替换的话自己创建一个HiddenHttpMethodFilter即可,Spring根据条件匹配优先使用用户自己创建的。

package com.atguigu.boot.config;

@Configuration(proxyBeanMethods = false) //单实例
public class WebConfig {

    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
        methodFilter.setMethodParam("_m"); //修改方法名
        return methodFilter;
    }
}

请求映射原理

看一下SpringBoot中DispatcherServelt的继承链:

image.png

SpringMVC功能分析都从org.springframework.web.servlet.DispatcherServlet中的doDispatch()方法出发:

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);

				// 找到当前请求使用哪个Handler(Controller的方法)处理
				mappedHandler = getHandler(processedRequest);
                
                //HandlerMapping:处理器映射。/xxx->>xxxx
                ...

所有的请求映射都在HandlerMapping中。例如RequestMappingHandlerMapping保存了所有@RequestMappinghandler的映射规则:

image-20210227114310304

💡HandlerMapping其实就保存了每个请求谁来处理。

请求进来,挨个尝试所有的HandlerMapping看是否有请求信息,如果有就找到这个请求对应的handler,如果没有就继续遍历下一个 HandlerMapping

需要自定义映射处理时,也可以自己给容器自定义HandlerMapping

⭐️请求传参与基本注解

SpringBoot几乎全注解开发,仅在参数列表中使用注解,SpringBoot就会自动帮这些参数自动确定好值并传入。

基本注解

注解作用
@PathVariable路径变量
@RequestHeader获取请求头
@ModelAttribute获取request域属性
@RequestParam获取请求参数
@MatrixVariable矩阵变量
@CookieValue获取cookie值
@RequestBody获取请求体

每个注解具体该怎么用点开注解源码参考其介绍即可:

image-20210302162922102
⭐️@PathVariable

获取路径变量,注意路径变量和请求参数参数做区分

  • 路径变量:/car/{id}/owner/{username}
  • 请求参数:?car=id&owner=username
@GetMapping("/car/{id}/owner/{username}") //@PathVariable获取路径上的路径变量,可以有多个
public Map<String, Object> getCar(@PathVariable("id") Integer id, @PathVariable("username") String username,
                                  @PathVariable Map<String, String> pv //把所有变量的值封装到Map中
){
    Map<String, Object> map = new HashMap<>();
    map.put("id", id);
    map.put("username", username);
    map.put("pv", pv);
    return map;
}
@RequestHeader

用来获取请求头相关的信息。

//测试@GetMapping
@GetMapping("/headers")
public Map<String, String> getCar(@RequestHeader("User-Agent")String userAgent, //获取哪个属性就写哪个名字
                                  @RequestHeader Map<String, String> headers){  //把所有请求头相关信息封装到Map中
    Map<String, Object> map = new HashMap<>();
    map.put("User-Agent", userAgent);
    map.put("headers", headers);
    return headers;
}
⭐️@RequestParam

用来获取请求参数,如www.xxx.com?car=id&owner=username

//测试@RequestParam
@RequestMapping("/params")
public Map<String, Object> testParams(@RequestParam("username") String username, @RequestParam("password") String password, @RequestParam Map<String, String> allParams //获取所有params
){
    Map<String, Object> map = new HashMap<>();
    map.put("username", username);
    map.put("password", password);
    map.put("allParams", allParams);
    return map;
}
@RequestBody
//测试@RequestBody,表单提交内容放在请求体中
@PostMapping("/save")
public Map postMethod(@RequestBody String content){
    Map<String, Object> map = new HashMap<>();
    map.put("content", content);
    return map;
}
❗️@RequestAttribute

用来获取request域属性,等同于request.getAtribute("xxx");,常用于页面转发时。

@ResponseBody
@GetMapping("/success")
//请求域中的所有东西都可以使用@RequestAttribute获取
public Map<String, Object> success(@RequestAttribute("msg") String msg, @RequestAttribute int code){
    Map<String, Object> map = new HashMap<>();
    map.put("msg", msg);
    return map;
}
❓@MatrixVariable

矩阵变量注解必须绑定在路径变量中才能被解析,注意域请求参数变量区分:

  • 请求参数:/cars/{path}?xxx=xxx&aaa=ccc
  • 矩阵变量:/cars/sell;low=34;brand=byd,audi,ydkey=value之间以 ; 分隔,一个key用多个value用 , 分割
//测试@MatrixVariable,访问语法:http://localhost:8080/cars/sell;low=34;brand=byd,audi,yd
@GetMapping("/cars/{path}")
public Map carSell(@MatrixVariable("low") Integer low, @MatrixVariable("brand") List<String> brand,
                   @PathVariable("path") String path){
    Map<String, Object> map = new HashMap<>();
    map.put("low", low);
    map.put("brand", brand);
    map.put("path", path);
    return map;
}

⚠️矩阵变量功能默认关闭,需要自己创建Bean修改配置来开启:

@Bean
public WebMvcConfigurer webMvcConfigurer(){
    return new WebMvcConfigurer() {
        @Override
        public void configurePathMatch(PathMatchConfigurer configurer) {
            UrlPathHelper urlPathHelper = new UrlPathHelper();
            //开启矩阵变量功能生效
            urlPathHelper.setRemoveSemicolonContent(false);
            configurer.setUrlPathHelper(urlPathHelper);
        }
    };
}
常见面试题中的应用

页面开发中cookie禁用了,session里面的内容怎么使用?
原本的过程是session.set(a,b)后产生jsessionid放在cookie中,每次发请求携带。
禁用后使用矩阵变量实现url重写,例如:/abc;jsesssionid=xxxx,把cookie的值使用矩阵变量的方式进行传递。

请求传参

除了使用注解来获取各种数据,Spring处理请求的方法的参数列表非常灵活,可以直接传入诸多复杂参数,方便用户操作。

1. 直接传入ServletAPI

请求映射的参数列表可以直接传入Servlet API来获取,包括WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId,非常方便。

底层由ServletRequestMethodArgumentResolver 负责解析以上的参数。

💡Spring所有的参数解析工作都由类似xxxxResolver的类来处理

2. 传入复杂参数

例如MapModelmapmodel里面的数据会被默认放在request的请求域中,相当于 request.setAttribute)、Errors/BindingResultRedirectAttributes(重定向携带数据)ServletResponseSessionStatusUriComponentsBuilderServletUriComponentsBuilder

Map<String,Object> map, Model model, HttpServletRequest request 都可以给request域中放入数据。

3. 传入自定义对象参数

SpringBoot支持直接在请求参数上传入自定义参数,**SpringBoot可以自动类型转换与格式化,可以级联封装。**页面提交的请求数据,无论是GET还是POST方式都可以和对象属性进行绑定。

🈚️参数处理原理

SpringBoot通过注解自动确定参数值的底层原理。

🈚️POJO封装原理

请求传入对象参数自动封装的底层原理。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值