Java充电社
专辑
博文
联系我
本人继续续收门徒,亲手指导
SpringMVC教程
-> @RequestHeader详解
1、Helloword
2、@Controller、@RequestMapping
3、接口测试利器
4、如何接受请求中的参数?
5、@RequestBody接收Json格式数据
6、多文件上传
7、返回页面常见的5种方式
8、返回json格式数据 & 通用返回值设计
9、SpringMVC返回null是什么意思?
10、异步处理请求
11、如何集成静态资源?
12、拦截器怎么用?
13、统一异常处理
14、实战篇:通用返回值 & 异常处理设计
15、全注解的方式 & 原理解析
16、源码解析SpringMVC处理请求的流程
17、源码解析SpringMVC容器的启动过程
18、RequestBodyAdvice:对@ReuqestBody进行增强
19、ResponseBodyAdvice:对@ResponseBody进行增强
20、RESTful接口详解
21、接口调用利器RestTemplate
22、参数解析器HandlerMethodArgumentResolver解密
23、@RequestParam用法及原理详解
24、@RequestBody原理解密
25、@RequestHeader详解
26、@CookieValue详解
27、@RequestAttribute详解
28、@SessionAttribute详解
29、重定向和转向详解
30、Converter转换器详解
31、跨域问题详解
32、类容协商,颠覆你的认知
33、终章
34、CORS通信
35、浏览器安全策略 & CORS
36、Http中的Content-Type详解
上一篇:@RequestBody原理解密
下一篇:@CookieValue详解
<div style="display:none"></div> **大家好,我是路人,这是SpringMVC系列第25篇。** 当我们在接口中想获取请求头的值的时候,怎么写代码更简单呢? SpringMVC中提供了@RequestHeader注解,用来获取请求头中的信息。 本文将介绍@RequestHeader的4种用法及原理。 ## 1、预备知识 1. <a href="https://mp.weixin.qq.com/s/83zvde-O5PlI9Vd2nP_PZQ" target="_blank">接口测试利器 HTTP Client</a> 2. <a href="https://mp.weixin.qq.com/s/XQ1K4eXtGvfG_0RReFVg8Q" target="_blank">参数解析器HandlerMethodArgumentResolver解密</a> ## 2、@RequestHeader介绍 @RequestBody注解源码如下,可以用来标注在接口的参数上,用来获取HTTP请求header中的值,下面通过案例列出常见的4种用法。 ```java @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequestHeader { /** * header的名称 */ @AliasFor("name") String value() default ""; /** * 同value,指定header的名称 */ @AliasFor("value") String name() default ""; /** * 是否是必须的,默认为ture,若指定的name在请求中不存在且未指定默认值defaultValue,则会出现异常 */ boolean required() default true; /** * 默认值 */ String defaultValue() default ValueConstants.DEFAULT_NONE; } ``` **推荐阅读:**[尚硅谷 Java 学科全套教程(总 207.77GB)](https://mp.weixin.qq.com/s?__biz=MzkzOTI3Nzc0Mg==&mid=2247484964&idx=2&sn=c81bce2f26015ee0f9632ddc6c67df03&scene=21#wechat_redirect) ## 3、用法1:指定name,获取指定的header ### 3.1、接口代码 参数使用@RquestHeader标注,并指定name属性的值,可以获取请求头中name对应头的值。 test1方法有3个参数: - name:用来获取头中name的值,required为false,表示不是必须的,并设置了defaultValue=ready,如果name不传递,则默认值为ready - age:相当于`Integer.parseInt(request.getHeader("age"))` - header1:类型为List,相当于`Arrays.asList(request.getHeaders("header1"))` ```java @RequestMapping("/requestheader/test1") public Map<String, Object> test1(@RequestHeader(value = "name", required = false, defaultValue = "ready") String name, @RequestHeader(value = "age") int age, @RequestHeader(value = "header1") List<String> header1) { Map<String, Object> result = new LinkedHashMap<>(); result.put("name", name); result.put("age", age); result.put("header1", header1); return result; } ``` 下面来测试3种场景,验证效果 ### 3.2、用例1:所有header都传递 ```java ### 所有头都传递 GET http://localhost:8080/chat18/requestheader/test1 name: java age: 25 header1: java Header1: spring ``` ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/236/d5dd5972-e7e2-4c7f-8c24-05980146869a.png) 点击红框旁边的绿色箭头运行,输出 ```json { "name": "java", "age": 25, "header1": [ "java", "spring" ] } ``` ### 3.3、用例2:name头不传递,会使用默认值 我们将头中的name干掉,相当于方法的第一个参数不传递,看看什么效果 ```java ### 头name不传递,会使用默认值 GET http://localhost:8080/chat18/requestheader/test1 age: 25 header1: java header1: spring ``` 运行输出如下,name取了默认值ready ```json { "name": "ready", "age": 25, "header1": [ "java", "spring" ] } ``` ### 3.4、用例3:required为true的,没有默认值的,不传递,会报错 大家注意第二个参数`@RequestHeader(value = "age") int age`,`required`属性取的默认值true,表示请求头中必须有age,否则会报错,我们直接在浏览器中访问接口,报错了,400错误,详细信息如下,大家熟悉下,以后遇到的时候,可以快速定位 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/236/a431c8b2-434f-4287-801d-016060282e3b.png) **推荐阅读:**[2021 最新版 Java 微服务学习线路图 + 视频](https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484192&idx=1&sn=505f2faaa4cc911f553850667749bcbb&scene=21#wechat_redirect) ## 4、用法2:不指定name属性,参数为Map类型,用来接收所有头 ### 4.1、用法 当我们想获取所有头的时候,参数上使用@RequestHeader标注,参数类型为Map<String,String>类型,用来接收所有请求头 ### 4.2、代码 ```java /** * {@link RequestHeader}不指定name,用于接收所有header的值, * 参数类型为Map<String,String>:key为头的名称,value为header的值 * * @param headerMap * @return */ @RequestMapping("/requestheader/test2") public Map<String, String> test2(@RequestHeader Map<String, String> headerMap) { return headerMap; } ``` ### 4.3、调用接口 ```java ### 用法2:未指定name属性,参数为Map<String,Strintg>类型,用来接收所有头 GET http://localhost:8080/chat18/requestheader/test2 name: java age: 25 header1: java header1: spring ``` ### 4.4、运行输出 上面调用接口的代码中传递了4个头,输出中却多了几个,多出的几个是http client自动为我们加上的几个,大家不必在意。 这里输出中有个问题,header1我们传递了2个,输出中只输出了第一个的值,第二个丢失了,那是因为参数是Map<String,String>结构导致的,用法3和用法4会解决这个问题。 ```java { "name": "java", "age": "25", "header1": "java", "host": "localhost:8080", "connection": "Keep-Alive", "user-agent": "Apache-HttpClient/4.5.12 (Java/11.0.10)", "accept-encoding": "gzip,deflate" } ``` **推荐阅读:**[阿里技术大佬整理的《Spring 学习笔记.pdf》](https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484573&idx=1&sn=7f3d83892186c16c57bc0b99f03f1ffd&scene=21#wechat_redirect) ## 5、用法3:不指定name属性,参数为MultiValueMap类型,用来接收所有头 ### 5.1、用法 用法2中,使用Map接收的时候,有重复的头的时候,同名的只会取一个,后面的会丢失,如何解决这个问题呢? 将参数类型改为MultiValueMap即可解决,MultiValueMap相当于Map<String,List<String>>,值是一个集合。 ```java /** * 参数为如果为 org.springframework.core.io.Resource 类型, * 则只能为Resource的[ByteArrayResource,InputStreamResource]这2种子类型: * * @param body * @return * @throws IOException */ @RequestMapping("/requestbody/test3") public String test3(@RequestBody InputStreamResource body) throws IOException { String content = IOUtils.toString(body.getInputStream(), "UTF-8"); System.out.println("content:" + content); return "ok"; } ``` ### 5.2、调用接口 ```java ### 用法3:不指定name属性,参数为MultiValueMap类型,用来接收所有头 GET http://localhost:8080/chat18/requestheader/test3 name: java age: 25 header1: java header1: spring ``` ### 5.3、运行输出 ```json { "name": [ "java" ], "age": [ "25" ], "header1": [ "java", "spring" ], "host": [ "localhost:8080" ], "connection": [ "Keep-Alive" ], "user-agent": [ "Apache-HttpClient/4.5.12 (Java/11.0.10)" ], "accept-encoding": [ "gzip,deflate" ] } ``` **推荐阅读:**[阿里大佬的《MySQL 学习笔记高清.pdf》](https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484544&idx=2&sn=c1dfe907cfaa5b9ae8e66fc247ccbe84&scene=21#wechat_redirect) ## 6、用法4:不指定name属性,参数为HttpHeaders类型,用来接收所有头 ### 6.1、HttpHeaders 上面的几种用法不是特别方便,比如获取到所有的header之后,此时我想获取某个头,那么我需要调用map.get(头名)来获取,此时我们可以将参数类型改为HttpHeaders,用这种类型,那么就方便了,先来看看这个类是什么玩意。 SpringMVC为咱们提供了一个更牛逼的类:HttpHeaders,用来表示http请求头,这个类中有很多好东西 - 实现了MultiValueMap<String,String>接口,而MultiValueMap实现了Map接口,相当于Map<String,List<String>> - 定义了大量header名称常量,基本所有常见的header名称,这个里面都有 - 定义了大量获取各种header的方法,比如`List<MediaType> getAccept()`等等,非常好用 - 还定义了大量set方法,用来设置头的信息 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/236/3d4a583a-5bb9-4c6a-b885-8444480435d8.png) ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/236/611281f5-1f13-42c2-8073-6752fa9adba2.png) ### 6.1、用法:参数为HttpHeaders类型,接收所有请求头 ```java /** * {@link RequestHeader}不指定name,用于接收所有header的值, * 参数类型为HttpHeaders,实现了MultiValueMap接口,HttpHeaders中提供了大量的获取头信息的各种方法,使用特别方便 * * @param httpHeaders * @return */ @RequestMapping("/requestheader/test4") public Map<String,List<String>> test4(@RequestHeader HttpHeaders httpHeaders) { Map<String,List<String>> result = new LinkedHashMap<>(httpHeaders); return result; } ``` ### 6.2、调用接口 ```java ### 用法4:不指定name属性,参数为HttpHeaders类型,用来接收所有头 GET http://localhost:8080/chat18/requestheader/test4 name: java age: 25 header1: java header1: spring ``` ### 6.3、运行输出 ```java { "name": [ "java" ], "age": [ "25" ], "header1": [ "java", "spring" ], "host": [ "localhost:8080" ], "connection": [ "Keep-Alive" ], "user-agent": [ "Apache-HttpClient/4.5.12 (Java/11.0.10)" ], "accept-encoding": [ "gzip,deflate" ] } ``` **推荐阅读:**[2021 版 java 高并发常见面试题汇总.pdf](https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485167&idx=1&sn=48d75c8e93e748235a3547f34921dfb7&scene=21#wechat_redirect) ## 7、@RequestHeader原理 方法参数上标注了@RequestHeader注解的,参数的值的获取主要有2个类来处理 - 指定了name的会被`org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver`处理 - 未指定name的,会被`org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver`处理 后面这个类的源码贴出来让大家瞅瞅,特别的简单,主要就2个方法,第一个方法用来判断是否支持解析当前参数,判断规则就是看参数上是否有`RequestHeader`注解及参数的类型是否为Map类型。 resolveArgument方法用来解析参数,代码就不解释了,简单明了,一看就懂,另外一个类`RequestHeaderMethodArgumentResolver`代码就不贴了,大家也自己去看看。 ```java public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return (parameter.hasParameterAnnotation(RequestHeader.class) && Map.class.isAssignableFrom(parameter.getParameterType())); } @Override public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { Class<?> paramType = parameter.getParameterType(); if (MultiValueMap.class.isAssignableFrom(paramType)) { MultiValueMap<String, String> result; if (HttpHeaders.class.isAssignableFrom(paramType)) { result = new HttpHeaders(); } else { result = new LinkedMultiValueMap<>(); } for (Iterator<String> iterator = webRequest.getHeaderNames(); iterator.hasNext();) { String headerName = iterator.next(); String[] headerValues = webRequest.getHeaderValues(headerName); if (headerValues != null) { for (String headerValue : headerValues) { result.add(headerName, headerValue); } } } return result; } else { Map<String, String> result = new LinkedHashMap<>(); for (Iterator<String> iterator = webRequest.getHeaderNames(); iterator.hasNext();) { String headerName = iterator.next(); String headerValue = webRequest.getHeader(headerName); if (headerValue != null) { result.put(headerName, headerValue); } } return result; } } } ``` 关于`@RequestHeader`的用法,大家其实可以去看看其源码中注释,注释中已经详细说了其用法,如下图,遇到其他的类的时候,也可以先看注释。 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/236/9e6bebad-2659-4eb7-a1e6-6adc43a3e86f.png) **推荐阅读:**[Idea 快捷键大全.pdf](https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485664&idx=1&sn=435f9f515a8f881642820d7790ad20ce&scene=21#wechat_redirect) ## 8、代码位置及说明 ### 8.1、git地址 ```html https://gitee.com/javacode2018/springmvc-series ``` ### 8.2、本文案例代码结构说明 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/236/8d23fbb8-8c14-44c3-a6f1-6828c331f546.png) <a style="display:none" target="_blank" href="https://mp.weixin.qq.com/s/_S1DD2JADnXvpexxaBwLLg" style="color:red; font-size:20px; font-weight:bold">继续收门徒,亲手带,月薪 4W 以下的可以来找我</a> ## 最新资料 1. <a href="https://mp.weixin.qq.com/s?__biz=MzkzOTI3Nzc0Mg==&mid=2247484964&idx=2&sn=c81bce2f26015ee0f9632ddc6c67df03&scene=21#wechat_redirect" target="_blank">尚硅谷 Java 学科全套教程(总 207.77GB)</a> 2. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484192&idx=1&sn=505f2faaa4cc911f553850667749bcbb&scene=21#wechat_redirect" target="_blank">2021 最新版 Java 微服务学习线路图 + 视频</a> 3. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484573&idx=1&sn=7f3d83892186c16c57bc0b99f03f1ffd&scene=21#wechat_redirect" target="_blank">阿里技术大佬整理的《Spring 学习笔记.pdf》</a> 4. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484544&idx=2&sn=c1dfe907cfaa5b9ae8e66fc247ccbe84&scene=21#wechat_redirect" target="_blank">阿里大佬的《MySQL 学习笔记高清.pdf》</a> 5. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485167&idx=1&sn=48d75c8e93e748235a3547f34921dfb7&scene=21#wechat_redirect" target="_blank">2021 版 java 高并发常见面试题汇总.pdf</a> 6. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485664&idx=1&sn=435f9f515a8f881642820d7790ad20ce&scene=21#wechat_redirect" target="_blank">Idea 快捷键大全.pdf</a> ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/1/2883e86e-3eff-404a-8943-0066e5e2b454.png)
#custom-toc-container