spring boot补习系列之几种scope详解
目标
- 了解http 请求/响应头及常见的属性;
- 了解如何使用springboot处理头信息 ;
- 了解如何使用springboot处理cookie ;
- 学会如何对 session 进行读写;
- 了解如何在不同请求间传递 flash参数
一、http 头信息
http 头(header)是一种附加内容,独立于请求内容和响应内容。
http 协议中的大量特性都通过header信息交互来实现,比如内容编解码、缓存、连接保活等等。
如下面的一个请求响应:
request
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
accept-encoding: gzip, deflate
accept-language: zh-cn,zh;q=0.9
cache-control: max-age=0
connection: keep-alive
host: www.cnblogs.com
if-modified-since: wed, 18 jul 2018 13:47:45 gmt
upgrade-insecure-requests: 1
user-agent: mozilla/5.0 (windows nt 6.1; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/65.0.3325.181 safari/537.36
名称 | 用途 |
---|---|
accept | 客户端期望的mime 类型列表 |
accept-encoding | 客户端期望的编解码方式 |
accept-language | 客户端期望的语言 |
cache-control | 缓存控制 |
connection | 连接行为(keep-alive) |
host | 请求访问的主机 |
if-modified-since | 缓存控制 |
upgrade-insecure-requests | 支持安全加密标记 |
user-agent | 用户代理(客户端标识) |
response
cache-control: private, max-age=10
connection: keep-alive
content-encoding: gzip
content-type: text/html; charset=utf-8
date: wed, 18 jul 2018 13:47:51 gmt
expires: wed, 18 jul 2018 13:48:01 gmt
last-modified: wed, 18 jul 2018 13:47:51 gmt
transfer-encoding: chunked
vary: accept-encoding
x-frame-options: sameorigin
x-ua-compatible: ie=10
名称 | 用途 |
---|---|
cache-control | 缓存控制 |
connection | 连接行为(keep-alive) |
content-encoding | 编解码方式 |
content-type | 内容类型(mime) |
date | 当前响应时间 |
expires | 文档过期时间 |
last-modified | 最后一次更新时间 |
transfer-encoding | 传输编码方式 |
vary | 需要刷新的请求header |
x-frame-options | frame展示策略(用于同源控制) |
x-ua-compatible | ie兼容属性 |
更多的** http header **可以从这里找到
二、springboot 处理头信息
前面的内容中已经讲过如何完成controller方法及请求的映射。
在springboot可通过@requestheader注解方式
将请求头信息映射到参数,如下面的片段:
@getmapping("/some") @responsebody public string someheader(@requestheader(value = "host") string host, @requestheader(value = "user-agent") string useragent, @requestheader(value = "cache-control", required = false) string cachecontrol, httpservletresponse response) { logger.info("host:{}", host); logger.info("user-agent:{}", useragent); logger.info("cache-control:{}", cachecontrol); // 设置响应头 response.setheader("cache-control", "no-cache,no-store,must-revalidate"); response.setheader("pragma", "no-cache"); response.setdateheader("expires", 0); return "ok"; }
而响应头呢,可以通过声明一个httpservletresponse参数后,
通过该对象进行设置,上面的代码非常容易理解。
如果希望获得全部的请求头,可以使用httpheaders对象:
@getmapping("/all") public responseentity<map<string, list<string>>> allheaders(@requestheader httpheaders headers) { map<string, list<string>> valuemap = new hashmap<string, list<string>>(); for (string header : headers.keyset()) { valuemap.put(header, headers.get(header)); logger.info("header[{}]={}", header, headers.get(header)); } // 通过responseentity设置响应头 responseentity<map<string, list<string>>> entity = responseentity.status(httpstatus.ok) .header("new header", uuid.randomuuid().tostring()).body(valuemap); return entity; }
上面的一段代码中,可以将所有请求头信息全部打印出来。
此外还须注意到,返回响应使用了responseentity对象,这是一个用于直接表示
响应信息头、内容的对象,利用responseentity可以很方便的设置响应头信息。
三、cookie处理
cookie一开始服务器用于辨别用户信息而记录在浏览器上的信息。
到目前为止cookie作为客户端的存储有了非常多的应用场景。
springboot 提供了@cookievalue以支持参数方式注入,如下: @getmapping("/some") @responsebody public string somecookie(@cookievalue(value = "counter", defaultvalue = "0") int counter, httpservletresponse response) { logger.info("counter:{}", counter); counter += 1; string newvalue = counter + ""; // 设置cookie response.addcookie(new cookie("counter", newvalue)); return newvalue; }
上述代码中,访问/some 可以获得一个counter的cookie值,
且每访问一次则自增一次,这是一个简单的访问计数器功能。
如果希望获取全部的cookie,可以参考以下代码:
@getmapping("/all") public responseentity<map<string, string>>allcookies(httpservletrequest request, httpservletresponse response) { map<string, string> valuemap = new hashmap<string, string>(); for (cookie cookie : request.getcookies()) { valuemap.put(cookie.getname(), cookie.getvalue()); logger.info("cookie[{}]={}", cookie.getname(), cookie.getvalue()); } // 设置cookie response.addcookie(new cookie("key", uuid.randomuuid().tostring())); return new responseentity<map<string, string>>(valuemap, httpstatus.ok); }
清理全部cookie
@getmapping("/clear") public responseentity<map<string, string>> clearcookies(httpservletrequest request, httpservletresponse response) { map<string, string> valuemap = new hashmap<string, string>(); for (cookie cookie : request.getcookies()) { valuemap.put(cookie.getname(), cookie.getvalue()); logger.info("cookie[{}]={}", cookie.getname(), cookie.getvalue()); // 清除 cookie.setmaxage(0); response.addcookie(cookie); } return new responseentity<map<string, string>>(valuemap, httpstatus.ok); }
cookie机制存在一定的缺陷,尽可能在考虑一些风险后使用
安全性无法保证,除非使用https;
浏览器端只有4kb大小的存储上限;
四、session处理
session 指的是会话,是建立于cookie机制上的一种身份识别方式。
由于cookie自身的安全性和容量限制,大多数应用中是在cookie中存放一个唯一凭证;
服务侧通过凭证再进行身份信息的存取,这就是会话的由来。
不同的语言、框架采用的实现方式有些差异,比如javaee采用jsession_id,而php则是phpsessid
session的交互原理可以参考下面一个图:
springboot 内嵌了servlet容器,则是沿用的jsession_id。因此在浏览一些javaweb站点时会发现该cookie。
使用@sessionattribute可以将会话中的属性映射到方法参数;
如果希望对session属性进行操作,可以在controller上声明@sessionattributes注解以指定想要变更的属性;
之后,通过model参数进行写入即可(由框架自动检测并修改session)
@sessionattributes("seed") public class sessioncontroller { private static final logger logger = loggerfactory.getlogger(sessioncontroller.class); @getmapping("/some") @responsebody public string somesession(@sessionattribute(value = "seed", required = false) integer seed, model model) { logger.info("seed:{}", seed); if (seed == null) { seed = (int) (math.random() * 10000); } else { seed += 1; } model.addattribute("seed", seed); return seed + ""; }
上面的例子与cookie实现访问计数器的功能是一样的!
如果希望获取全部会话,可以使用httpsession
@getmapping("/all") public responseentity<map<string, object>> allsessions(httpsession session) { map<string, object> valuemap = new hashmap<string, object>(); enumeration<string> isession = session.getattributenames(); while (isession.hasmoreelements()) { string sessionname = isession.nextelement(); object sessionvalue = session.getattribute(sessionname); valuemap.put(sessionname, sessionvalue); logger.info("sessoin[{}]={}", sessionname, sessionvalue); } // 写入session session.setattribute("timestmap", new date()); return new responseentity<map<string, object>>(valuemap, httpstatus.ok); }
清除会话
@getmapping("/clear") public responseentity<map<string, object>> clearsessions(httpsession session) { map<string, object> valuemap = new hashmap<string, object>(); enumeration<string> isession = session.getattributenames(); while (isession.hasmoreelements()) { string sessionname = isession.nextelement(); object sessionvalue = session.getattribute(sessionname); valuemap.put(sessionname, sessionvalue); logger.info("sessoin[{}]={}", sessionname, sessionvalue); session.removeattribute(sessionname); } return new responseentity<map<string, object>>(valuemap, httpstatus.ok); }
五、flash参数传递
flash的意思是一瞬间,一闪而过的,因此很好理解,这是一类仅用来消费一次的参数,有些类似阅后即焚。
试想这样的场景,你确认完购物车,完成订单支付后进入订单管理界面,而此时界面上提示你"下单成功,请等待发货"。
这便可以通过flash传参来实现。
flash的意义是用作请求之间的瞬时参数传递,仅消费一次后便不再用。
以下是一个示例:
/** * 执行跳转,并设置传值 * * @param counter * @param response * @return */ @getmapping("/first") public string first(final redirectattributes redirectattrs) { logger.info("redirect start:{}"); redirectattrs.addflashattribute("flash", uuid.randomuuid().tostring()); return "redirect:/flash/second"; } /** * 获取传值 * * @param session * @param response * @return */ @getmapping("/second") @responsebody public string second(@modelattribute("flash") string flash) { logger.info("redirect receive {}", flash); return flash; }
交互原理
sprintboot中flash机制也是利用session实现的,其中flashmapmanager接口实现了flash参数的管理。
默认的实现是sessionflashmapmanager,可以通过requestcontextutils获得上下文中的flashmapmanager对象。
requestcontextutils通过request scope(请求上下文)存取对象
这也是一个本文未提及的scope域,request上下文是利用线程变量实现的,通常用于线程内业务处理的数据交互。
小结
http 头信息是一种附加内容,用于实现http协议中的各种特性,在开始部分介绍了常见的头信息定义。
本文主要介绍了几种常见的http scope信息的存取方法,包括如何对header、cookie进行读取及修改。
springboot 内嵌了servlet容器,会话处理机制上沿用了jsessionid,通过代码示例介绍了会话的处理方法;
flash参数是一种阅后即焚的数据,其底层实现也用了session的实现方案。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。