SpringMVC接收HTTP请求参数总结

请求参数所在位置

先来说说URL是什么,它是定义网络上某个特定的资源,说明它位于何处,以及如何获取。

它的通用语法格式是:

<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<fragment>

具体到Spring MVC处理http(s)协议的场景中,其应用语法得到了简化:

http(s)://<host>:<port>/<path>?<query>#<fragment>
  1. 数据交互协议是http或者https,这是肯定的,不用多说
  2. 去掉了通用语法格式中的<params>参数,所有需要的URL参数都采用<query>参数传递
  3. <fragment>是锚点参数,其作用范围仅限于前端浏览器代理等,请求的时候是不能传递给后端服务器的,这是出于网络安全的考虑

再考虑到HTTP协议请求报文格式的三个部分(请求行、请求头、请求体):

<method> <request-URL> <version>
<headers>

<entity-body>

基本就可以有个总结,Spring MVC能够获取HTTP请求参数的位置应该有四个:

  1. 获取<request-URL>中的path参数或者query参数
  2. 获取<headers>中的请求头参数
  3. 获取<entity-body>中的请求体参数
  4. 获取cookies中的参数,其本质也是请求头传参

接下来的问题就是了解Spring MVC的编码规范了。

path参数

/* 
    请求报文:
    
    GET /user/zhangsan/12 HTTP/1.1
    Host: localhost:8080  
*/
@GetMapping("/user/{name}/{age}")
public void path(@PathVariable String name, @PathVariable String age) {
    logger.info("name = {}, age = {}", name, age);
}

// name = zhangsan, age = 12

因为path参数位于URL当中,Spring MVC就采用占位符{xxx}的方式,将参数名称和参数值对应,再结合@PathVariable注解实现参数变量的注入。

采用path传递参数,特别适合设计restful接口。

还有另外一种稍微奇葩一点的写法,实际用处可能不大,仅做了解:

/*
    请求报文:
    
    GET /userzhangsan/12 HTTP/1.1
    Host: localhost:8080
*/
@GetMapping("/user{name}/{age}")
public void path(@PathVariable String name, @PathVariable String age) {
    logger.info("name = {}, age = {}", name, age);
}

// name = zhangsan, age = 12

注意区别,这种写法在URL路径中,userzhangsan是连起来的,中间没有/划分,Spring MVC接收的时候,就要写成user{name}形式,相当于加了个限制,所有name参数的前缀都必须是user字符串。

query参数

/*
    请求报文:
    
    GET /user?name=zhangsan&age=12 HTTP/1.1
    Host: localhost:8080
*/
@GetMapping("/user")
public void query(String name,String age) {
    logger.info("name = {}, age = {}", name, age);
}

// name = zhangsan, age = 12

请求中采用了query参数格式,其值为?name=zhangsan&age=12,以?标记作为query参数的开始,以&分割多个参数。

方法参数没有加Spring注解,默认注解是@RequestParam,加不加都行。

加上@RequestParam注解的好处是,方法参数名定义更自由:

@GetMapping("/user")
public void path1(@RequestParam(value = "name") String person,@RequestParam String age) {
    logger.info("name = {}, age = {}", person, age);
}

示例中将方法参数分开写成了两个,如果query参数太多,还可以定义一个Java Bean,将对应query参数作为Bean的字段,然后以Bean作为方法参数:

@Data
public class User {
    private String name;
    private String age;
}

@GetMapping("/user")
public void query(User user) {
    logger.info("name = {}, age = {}", user.getName(), user.getAge());
}

需要注意的是,这里绝对不能再加注解@RequestParam,因为这里方法参数名称是user,跟query参数名称是不对应的。

不加注解,Spring MVC可以自动将多个query参数注入到user对象属性当中,属性名需要和参数名保持一致。

header参数

/*
    请求报文:
    
    GET /user?name=zhangsan&age=12 HTTP/1.1
    Host: localhost:8080
*/
@GetMapping("/user")
public void header(@RequestHeader(value = "Host") String host) {
    logger.info("host = {}", host);
}

// host = localhost:8080

这是获取某个具体请求头参数的便捷方法,主要就是根据注解@RequestHeader达到目的。

如果要获取所有请求头,那还是通过最原始的HttpServletRequest对象实现吧:

@GetMapping("/user")
public void header(HttpServletRequest request) {
    Enumeration<String> headerNames = request.getHeaderNames();
    while (headerNames.hasMoreElements()) {
        String key = headerNames.nextElement();
        logger.info("{} = {}", key, request.getHeader(key));
    }
}

body参数

HTTP协议中除了POSTPUT方法具有请求体以外,其它方法都没有请求体。所以,这一节内容只适用于POSTPUT请求。

通过pathquery还有header,可以传递一些简单的参数,如果要传递的参数比较复杂,还是放在请求体中比较好处理。

因为请求体可以承载复杂的参数传递,所以请求体的数据类型有多种分类,常用的三种:

  1. application/x-www-form-urlencoded
  2. application/json
  3. multipart/form-data

他们都适用于简单或复杂文本数据的传递,可如果参数中包含文件,则必须使用第三种form-data格式。

application/x-www-form-urlencoded

/*
    请求报文:
    
    POST /user HTTP/1.1
    Host: localhost:8080
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 20
    
    name=zhangsan&age=12
*/
@PostMapping("/user")
public void body(String name, String age) {
    logger.info("name = {}, age = {}", name, age);
}

// name = zhangsan, age = 12

这里的参数在body里面,是application/x-www-form-urlencoded类型,类似的,通过方法参数名和请求参数名的对应绑定关系,就可以得到参数值。

有一点需要特别注意!如果querybody里面包含同名参数,那么将会同时得到两处参数,很多时候,这并不是我们想要的结果:

/*
    请求报文:
    
    POST /user?name=lisi&age=16 HTTP/1.1
    Host: localhost:8080
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 20
    
    name=zhangsan&age=12
*/
@PostMapping("/user")
public void body(String name, String age) {
    logger.info("name = {}, age = {}", name, age);
}

// name = lisi,zhangsan, age = 16,12

@PostMapping("/user")
public void body(User user) {
    logger.info("name = {}, age = {}", user.getName(), user.getAge());
}

// name = lisi,zhangsan, age = 16,12

将两个独立的方法参数封装到一个Java Bean中,结果也是一样的。

如果出现这种情况,我认为这属于在Spring领域内对HTTP协议的滥用,应该纠正一下我们的使用规范。

传递参数可以在querybody两个部分二选一,但是尽量还是不要同时使用,如果一定要使用,最好也不要出现同名参数。

由此可见Spring MVC也有其不足之处,它对HTTP协议的使用无形中添加了很多的限制。

application/json

/*
    请求报文:
    
    POST /user?name=lisi&age=16 HTTP/1.1
    Host: localhost:8080
    Content-Type: application/json
    Content-Length: 41
    
    {
        "name": "zhangsan",
        "age": 12
    }
*/

@PostMapping("/user")
public void body(User user) {
    logger.info("name = {}, age = {}", user.getName(), user.getAge());
}

// name = lisi, age = 16

@PostMapping("/user")
public void body(@RequestBody User user) {
    logger.info("name = {}, age = {}", user.getName(), user.getAge());
}

// name = zhangsan, age = 12

这里写了两个方法,一个加了@RequestBody注解,一个没加注解,通过日志可以看出,取值的结果是不一样的。

没加注解的从<query>中取值注入了,加了注解的从<entity-body>中取值注入了。

multipart/form-data

/*
    请求报文:
    
    POST /user?name=lisi&age=16 HTTP/1.1
    Host: localhost:8080
    Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
    Content-Length: 21196
    
    ----WebKitFormBoundary7MA4YWxkTrZu0gW
    Content-Disposition: form-data; name="name"

    zhangsan
    ----WebKitFormBoundary7MA4YWxkTrZu0gW
    Content-Disposition: form-data; name="age"

    12
    ----WebKitFormBoundary7MA4YWxkTrZu0gW
    Content-Disposition: form-data; name="file"; filename="1.jpg"
    Content-Type: image/jpeg

    (data)
    ----WebKitFormBoundary7MA4YWxkTrZu0gW
*/

@PostMapping("/user")
public void body(String name, String age, @RequestParam("file") MultipartFile file, HttpServletRequest request) {
    logger.info("name = {}, age = {}, file = {}", name, age, file.getOriginalFilename());
}

// name = zhangsan,lisi, age = 12,16, file = 1.jpg

@PostMapping("/user")
public void body(User user, @RequestParam("file") MultipartFile file) {
    logger.info("name = {}, age = {}, file = {}", user.getName(), user.getAge(), file.getOriginalFilename());
}

// name = zhangsan,lisi, age = 12,16, file = 1.jpg

在这种请求体类型中,不能使用注解@RequestBody,同样也会存在<query><entity-body>同时取值注入的问题。

cookies参数

/* 
    请求报文:
    
    GET /user/zhangsan/12 HTTP/1.1
    Host: localhost:8080
    Cookie: JSESSIONID=3jd6f92h47syx64h58f2nduyso1nz63a; id=12345678
*/

@GetMapping("/user")
public void cookies(@CookieValue("JSESSIONID") String sessionId) {
    logger.info("sessionId = {}", sessionId);
}

// sessionId = 3jd6f92h47syx64h58f2nduyso1nz63a

获取cookies也是加个注解@CookieValue就可以了。

如果想要获取所有的cookies,还是需要从HttpServletRequest对象中获取:

/* 
    请求报文:
    
    GET /user/zhangsan/12 HTTP/1.1
    Host: localhost:8080
    Cookie: JSESSIONID=3jd6f92h47syx64h58f2nduyso1nz63a; id=12345678
*/

@GetMapping("/user")
public void cookies(HttpServletRequest request) {
    Cookie[] cookies = request.getCookies();
    for (Cookie cookie : cookies) {
        logger.info("{} = {}", cookie.getName(),cookie.getValue());
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值