目標
- 了解http 請求/響應頭及常見的屬性;
- 了解如何使用springboot處理頭信息 ;
- 了解如何使用springboot處理cookie ;
- 學會如何對 session 進行讀寫;
- 了解如何在不同請求間傳遞 flash參數(shù)
一、http 頭信息
http 頭(header)是一種附加內(nèi)容,獨立于請求內(nèi)容和響應內(nèi)容。
http 協(xié)議中的大量特性都通過header信息交互來實現(xiàn),比如內(nèi)容編解碼、緩存、連接保活等等。
如下面的一個請求響應:
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 | 內(nèi)容類型(mime) |
date | 當前響應時間 |
expires | 文檔過期時間 |
last-modified | 最后一次更新時間 |
transfer-encoding | 傳輸編碼方式 |
vary | 需要刷新的請求header |
x-frame-options | frame展示策略(用于同源控制) |
x-ua-compatible | ie兼容屬性 |
更多的** http header **可以從這里找到
二、springboot 處理頭信息
前面的內(nèi)容中已經(jīng)講過如何完成controller方法及請求的映射。
在springboot可通過@requestheader注解方式
將請求頭信息映射到參數(shù),如下面的片段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@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參數(shù)后,
通過該對象進行設置,上面的代碼非常容易理解。
如果希望獲得全部的請求頭,可以使用httpheaders對象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@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對象,這是一個用于直接表示
響應信息頭、內(nèi)容的對象,利用responseentity可以很方便的設置響應頭信息。
三、cookie處理
cookie一開始服務器用于辨別用戶信息而記錄在瀏覽器上的信息。
到目前為止cookie作為客戶端的存儲有了非常多的應用場景。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
springboot 提供了 @cookievalue 以支持參數(shù)方式注入,如下: @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值,
且每訪問一次則自增一次,這是一個簡單的訪問計數(shù)器功能。
如果希望獲取全部的cookie,可以參考以下代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@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自身的安全性和容量限制,大多數(shù)應用中是在cookie中存放一個唯一憑證;
服務側(cè)通過憑證再進行身份信息的存取,這就是會話的由來。
不同的語言、框架采用的實現(xiàn)方式有些差異,比如javaee采用jsession_id,而php則是phpsessid
session的交互原理可以參考下面一個圖:
springboot 內(nèi)嵌了servlet容器,則是沿用的jsession_id。因此在瀏覽一些javaweb站點時會發(fā)現(xiàn)該cookie。
使用@sessionattribute可以將會話中的屬性映射到方法參數(shù);
如果希望對session屬性進行操作,可以在controller上聲明@sessionattributes注解以指定想要變更的屬性;
之后,通過model參數(shù)進行寫入即可(由框架自動檢測并修改session)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@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實現(xiàn)訪問計數(shù)器的功能是一樣的!
如果希望獲取全部會話,可以使用httpsession
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@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); } |
清除會話
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@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參數(shù)傳遞
flash的意思是一瞬間,一閃而過的,因此很好理解,這是一類僅用來消費一次的參數(shù),有些類似閱后即焚。
試想這樣的場景,你確認完購物車,完成訂單支付后進入訂單管理界面,而此時界面上提示你"下單成功,請等待發(fā)貨"。
這便可以通過flash傳參來實現(xiàn)。
flash的意義是用作請求之間的瞬時參數(shù)傳遞,僅消費一次后便不再用。
以下是一個示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
/** * 執(zhí)行跳轉(zhuǎn),并設置傳值 * * @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實現(xiàn)的,其中flashmapmanager接口實現(xiàn)了flash參數(shù)的管理。
默認的實現(xiàn)是sessionflashmapmanager,可以通過requestcontextutils獲得上下文中的flashmapmanager對象。
requestcontextutils通過request scope(請求上下文)存取對象
這也是一個本文未提及的scope域,request上下文是利用線程變量實現(xiàn)的,通常用于線程內(nèi)業(yè)務處理的數(shù)據(jù)交互。
小結(jié)
http 頭信息是一種附加內(nèi)容,用于實現(xiàn)http協(xié)議中的各種特性,在開始部分介紹了常見的頭信息定義。
本文主要介紹了幾種常見的http scope信息的存取方法,包括如何對header、cookie進行讀取及修改。
springboot 內(nèi)嵌了servlet容器,會話處理機制上沿用了jsessionid,通過代碼示例介紹了會話的處理方法;
flash參數(shù)是一種閱后即焚的數(shù)據(jù),其底層實現(xiàn)也用了session的實現(xiàn)方案。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。
原文鏈接:https://www.cnblogs.com/littleatp/p/9345801.html