More Related Content Similar to Http Headers 與 Cache 機制(2016) (20) Http Headers 與 Cache 機制(2016)3. AGENDA
1. 常見的 Http Headers
2. Cache 的使用情境與最佳化
3. Code example (.NET MVC - Web API)
Code example (Android - okhttp)
6. Http Header 是我們在向目標 Server要求或取得內容時,依附在上
面的一些屬性或參數
不管是使用哪一種 method,GET.POST.DELETE 還是 PUT 等等….
都可以使用 Header 來傳遞資料
Header 分為三種-Request(請求)、Response(回應)、General(通
用)
WHAT ARE HTTP HEADERS?
7. 名稱 範例 簡介
Date Wed, 20 Apr 2016 08:46:31
GMT
時間戳記
Cache-Control no-cache,max-age=30 表示使用何種 Cache 機制,經常使用的 directive
有:no-store , no-cache , public , private , max-age
Pragma no-cache 表示使用何種 Cache 機制(在Http 1.1後都改為參考
Cache-Control 了)
Expires -1 表示 Cache 時間(在Http 1.1後都改為參考 Cache-
Control 了)
General headers
8. 名稱 範例 簡介
Content-Length 145698 回應的內容長度
Content-Type application/json 回應的格式,常見的有text/html(網頁)、text/plain(無格
式純文字)、application/json(JSON格式)、
application/xml(XML格式)、text/xml(XML格式)
Content-Language en 回應的語言
Content-Encoding gzip 回應的內容壓縮方式
Server Microsoft-IIS/8.5 表示何種技術的 web server
ETag "12345678" Cache 機制中的*內容驗證權杖,需要用引號("")包住
Last-Modified Wed, 20 Apr 2016
08:46:31 GMT
Cache 機制中的*時間驗證權杖,需使用 Universal Time
X-Powered-By ASP.NET 表示何種技術的 web service,特別說明一下,X 開頭系
列的 Header 通常是非標準(各個語言自定義的),雖說是
非標準,但有些 X 開頭的 Header 幾乎是很常見的
X-Version 4.0.30319 web service 當前版本
Response headers
9. 名稱 範例 簡介
Accept */* 請求的內容的通用欄位
Accept-Charset UTF-8 請求的內容編碼
Accept-Encoding gzip, deflate 請求的內容壓縮方式
Accept-Language zh-TW,zh-CN;q=0.6,en-
US;q=0.2
請求的內容語言,q 代表請求權重(範圍值介於0~1之間,不
寫預設=1),以這個範例來說,我指定可以接受 zh-TW(繁
中)zh-CN(簡中)en-US(英文),其中又以 zh-TW 權重最高=1,
zh-CN 其次=0.6,en-US 最低=0.2
Authorization YWJjOjEyMw== 通常會放身份驗證 token,Http 1.0時的規範格式為base64
encode後的username:password,abc:123 → YWJjOjEyMw==
If-None-Match "12345678" 這裡會放上一次 Server 給的 ETag
If-Modified-Since Wed, 20 Apr 2016
08:46:31 GMT
這裡會放上一次 Server 給的 Last-Modified
Request headers
10. WHY CACHE?
1. 減少 request 次數
2. 降低 response 資料
• Glide
• Picasso
• Android Query
• Universal Image Loader
• ……
11. request
• no-cache
• no-store
• max-age = delta-seconds
• max-stale = delta-seconds
• min-fresh = delta-seconds
• no-transform
• only-if-cached
response
• public
• private
• no-store
• no-cache
• max-age = delta-seconds
• no-transform
• must-revalidate
• proxy-revalidate
• s-maxage = delta-seconds
Cache-Control directive
15. no-cache + ETag
Client Server
GET
200 OK
Cache-Control →no-cache, private
ETag →“123"
...
GET
If-None-Match→“123"
Checking
cache
304 Not Modified
16. no-cache + Last-Modified
Client Server
GET
200 OK
Cache-Control →no-cache, publice
Last-Modified→ Sat, 01 Jan 2000 00:00:00 GMT
...
GET
If-Modified-Since → Sat, 01 Jan 2000 00:00:00 GMT
Checking
cache
304 Not Modified
17. CODE EXAMPLE - BACKEND
Demo code : https://bitbucket.org/ct7ct7ct7/demo-mvc_testcache
Demo page: http://demo-test-cache.azurewebsites.net
18. public class UserMaxAgeController : ApiController {
public HttpResponseMessage Get() {
User user = TestCacheHelper.getUser();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, user);
response.Headers.CacheControl = new CacheControlHeaderValue() {
Private = true,
MaxAge = TimeSpan.FromSeconds(10)
};
return response;
}
}
ONLY MAX-AGE
19. public class UserNoCacheController : ApiController {
public HttpResponseMessage Get() {
User user = TestCacheHelper.getUser();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, user);
response.Headers.ETag = new EntityTagHeaderValue(TestCacheHelper.convertToEtag(user.GetHashCode()));
response.Content.Headers.LastModified = user.LastUpdatedAt.ToUniversalTime(); //GMT
response.Headers.CacheControl = new CacheControlHeaderValue() {
Private = true,
MaxAge = TimeSpan.FromSeconds(10)
};
var modifiedSince = Request.Headers.IfModifiedSince;
var eTag = Request.Headers.IfNoneMatch.FirstOrDefault();
if (modifiedSince != null && modifiedSince.Value.ToUniversalTime().Equals(user.LastUpdatedAt)) {
response.StatusCode = HttpStatusCode.NotModified; //304
}
if (eTag != null && eTag.ToString().Equals(TestCacheHelper.convertToEtag(user.GetHashCode()))) {
response.StatusCode = HttpStatusCode.NotModified; //304
}
return response;
}
}
ETAG + LAST-MODIFIED + MAX-AGE
20. CODE EXAMPLE - ANDROID
Demo code : https://bitbucket.org/ct7ct7ct7/demo-android_test_cache
21. private final static int CACHE_SIZE = 10 * 1024 * 1024; // 10 MiB
/*reset code…*/
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(BuildConfig.DEBUG ? HttpLoggingInterceptor.Level.BODY :
HttpLoggingInterceptor.Level.NONE);
okHttpClient = new OkHttpClient.Builder()
.cache(new Cache(Example1Activity.this.getExternalCacheDir(), CACHE_SIZE))
.addInterceptor(interceptor)
.build();
設定 cache size 與 directory path
EXAMPLE - 1
22. EXAMPLE - 2
/*reset code…*/
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Response originalResponse = chain.proceed(chain.request());
String eTag = originalResponse.headers().get("ETag");
String lastModified = originalResponse.headers().get("Last-Modified");
cacheSharedPreferences.set(originalRequest.method(),originalRequest.url().toString(),
CacheSharedPreferences.Type.E_TAG,eTag);
cacheSharedPreferences.set(originalRequest.method(),originalRequest.url().toString(),
CacheSharedPreferences.Type.LAST_MODIFIED,lastModified);
return originalResponse;
}
透過 interceptor 將 response header 中的 ETag 與 Last-Modified 記下來
23. EXAMPLE - 2
/*reset code…*/
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
String eTag =
cacheSharedPreferences.get(originalRequest.method(),originalRequest.url().toString(),
CacheSharedPreferences.Type.E_TAG);
String lastModified =
cacheSharedPreferences.get(originalRequest.method(),originalRequest.url().toString(),
CacheSharedPreferences.Type.LAST_MODIFIED);
Request compressedRequest = originalRequest.newBuilder()
.header("If-Modified-Since", lastModified)
.header("If-None-Match", eTag)
.build();
return chain.proceed(compressedRequest);
}
透過 interceptor 在 request header 中添加驗證權杖
If-Modified-Since 與 If-None-Match
24. EXAMPLE - 3
透過 interceptor 在request header中添加
Cache-Control -> max-stale
/*reset code…*/
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
if (CommonUtils.checkNetwork(context) == false) {
int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
Request compressedRequest = originalRequest.newBuilder()
.header("Cache-Control", "public, max-stale=" + maxStale)
.build();
return chain.proceed(compressedRequest);
}
return chain.proceed(originalRequest);
}
25. OKHTTP - INTERCEPTOR
Application Interceptor
• Log handler
• Exception error handler
• Cache handler
• …..
Network Interceptor
• Log handler
• Header or parameter handler
• Exception error handler
• ……
27. 1. 檢查網路狀態
1. 無→使用 cache 資料
2. 有→檢查 cache 使用期限
1. 期限內→使用 cache 資料
2. 已過期→添加驗證權杖在 header 中
發送 request 時: 收到 response 時:
1. 200→儲存資料及相關 header 至 cache 中
2. 304→使用 cache 資料
CACHE 最佳化策略
28. 1. Cache原則:減少 request 次數 & 降低 response 資料量
2. max-age 要謹慎使用
3. 為你的 api 加上內容驗證 ( ETag ) 與時間驗證 ( Last-Modified )
CONCLUSION
29. • Google Developer – HTTP caching
• O3noBLOG - Cache Control 與 ETag
• HTTP 1.1 RFC 2616 - 13 Caching in HTTP
• HTTP 1.1 RFC 2616 – 14 Header Field Definitions
• Soul & Shell Blog - 初探 HTTP 1.1 Cache 機制
• Okhttp - Wiki
REFERENCE
30. • Email: ct7ct7ct7@gmail.com
• Facebook:https://www.facebook.com/profile.php?id=100000140471745
• LinkedIn:https://goo.gl/j2bPIl
• Blog:https://anson-programming.blogspot.tw (安森瓦舍)
• 個人作品:https://play.google.com/store/apps/developer?id=CityOne