Spring编程常见错误–Spring Web篇-10 |Spring Web Header 解析常见错误

十、Spring Web Header 解析常见错误

对于一个 HTTP 请求而言,URL 固然重要,但是为了便于用户使用,URL 的长度有限,所能携带的信息也因此受到了制约。
如果想提供更多的信息,Header 往往是不二之举。不言而喻,Header 是介于 URL 和 Body 之外的第二大重要组成,它提供了更多的信息以及围绕这些信息的相关能力,例如 Content-Type 指定了我们的请求或者响应的内容类型,便于我们去做解码。虽然 Spring 对于 Header 的解析,大体流程和 URL 相同,但是 Header 本身具有自己的特点。例如,Header 不像 URL 只能出现在请求中。所以,Header 处理相关的错误和 URL 又不尽相同。

一.接受 Header 使用错 Map 类型

在 Spring 中解析 Header 时,我们在多数场合中是直接按需解析的。例如,我们想使用一个名为 myHeaderName 的 Header,我们会书写代码如下:

1、只有一个Header的情况


@RequestMapping(path = "/hi", method = RequestMethod.GET)
public String hi(@RequestHeader("myHeaderName") String name){
   //省略 body 处理
};

2、有很多个Header的情况

但是假设我们需要解析的 Header 很多时,按照上面的方式很明显会使得参数越来越多。在这种情况下,我们一般都会使用 Map 去把所有的 Header 都接收到,然后直接对 Map 进行处理。于是我们可能会写出下面的代码:


@RequestMapping(path = "/hi1", method = RequestMethod.GET)
public String hi1(@RequestHeader() Map map){
    return map.toString();
};

这种可以正常使用,但是如果面对这种请求的时候,结果就会超出预期,即一个Header多个value。

GET http://localhost:8080/hi1
myheader: h1
myheader: h2

此时我们执行请求,会发现返回的结果并不能将这两个值如数返回。结果示例如下:

{myheader=h1, host=localhost:8080, connection=Keep-Alive, user-agent=Apache-HttpClient/4.5.12 (Java/11.0.6), accept-encoding=gzip,deflate}

3、案例解析

对于一个多值的 Header,在实践中,通常有两种方式来实现,一种是采用下面的方式:

一个键,一个值(用,隔开)

Key: value1,value2

一个键多个值

Key:value1
Key:value2

对于方式 1,我们使用 Map 接口自然不成问题。但是如果使用的是方式 2,我们就不能拿到所有的值。

4、问题修正(建议使用HttpHeaders)

要完整接收到所有的 Header,不能直接使用 Map 而应该使用 MultiValueMap。

1.使用MultiValueMap
//方式 1
@RequestHeader() MultiValueMap map
2.使用HttpHeaders
/方式 2
@RequestHeader() HttpHeaders map

对比来说,方式 2 更值得推荐,因为它使用了大多数人常用的 Header 获取方法,例如获取 Content-Type 直接调用它的 getContentType() 即可,诸如此类,非常好用。

二.错认为 Header 名称首字母可以一直忽略大小写

1、直接获取Header的值

@RequestMapping(path = "/hi2", method = RequestMethod.GET)
public String hi2(@RequestHeader("MyHeader") String myHeader){
    return myHeader;
};

2、通过MAP形式获取Header的值

@RequestMapping(path = "/hi2", method = RequestMethod.GET)
public String hi2(@RequestHeader("MyHeader") String myHeader, @RequestHeader MultiValueMap map){
    return myHeader + " compare with : " + map.get("MyHeader");
};

直接获取 Header 是可以忽略大小写的,但是如果从接收过来的 Map 中获取 Header 是不能忽略大小写的。稍微不注意,我们就很容易认为 Header 在任何情况下,都可以不区分大小写来获取值。

3、案例解析

1.存取 Map 的 Header 是没有忽略大小写的
2.从 Map 中获取的 Header 也没有忽略大小写

4、问题修正

写的规范一点即可

三.试图在 Controller 中随意自定义 CONTENT_TYPE 等

1、代码分析


@RequestMapping(path = "/hi3", method = RequestMethod.GET)
public String hi3(HttpServletResponse httpServletResponse){
  httpServletResponse.addHeader("myheader", "myheadervalue");
  httpServletResponse.addHeader(HttpHeaders.CONTENT_TYPE, "application/json");
    return "ok";
};

访问后会得到如下结果

GET http://localhost:8080/hi3 HTTP/1.1 200myheader: myheadervalueContent-Type: text/plain;charset=UTF-8Content-Length: 2Date: Wed, 17 Mar 2021 08:59:56 GMTKeep-Alive: timeout=60Connection: keep-alive

可以看到 myHeader 设置成功了,但是 Content-Type 并没有设置成我们想要的"application/json",而是"text/plain;charset=UTF-8"。

2、案例解析(具体的返回值由多种因素决定,但是不应该在response中修改)

在 Spring Boot 基于内嵌 Tomcat 开发时并不一定能设置成功,最终返回的 Content-Type 是根据实际的返回值及类型等多个因素来决定的。

1.决定用哪一种 MediaType 返回
2.选择消息转化器并完成转化

3、问题修正

1.修改请求中的 Accept 头,约束返回类型

带上 Accept 头,这样服务器在最终决定 MediaType 时,会选择 Accept 的值。

GET http://localhost:8080/hi3
Accept:application/json
2.标记返回类型

主动显式指明类型,修改方法如下:


@RequestMapping(path = "/hi3", method = RequestMethod.GET, produces = {"application/json"})

即使用 produces 属性来指明即可。这样的方式影响的是可以返回的 Media 类型,一旦设置,下面的方法就可以只返回一个指明的类型了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值