LoginSignup
0
1

More than 5 years have passed since last update.

Goで動的htmlを一定時間キャッシュさせる(Last-Modified)

Last updated at Posted at 2019-02-14

動的な html をキャッシュさせる

基本的にブラウザは画像などのリソースについてはキャッシュするのですが、 HTML はキャッシュの対象外です。

キャッシュさせるためには、サーバー側からブラウザにキャッシュ可能であることを通知する必要があります。

今回はその方法を紹介します。

なぜ Etag ではなく Last-Modified なのか?

キャッシュさせる方法として Etag という方法もあります。

こちらはハッシュ値等をやり取りし、前回のハッシュと差分があった場合にキャッシュを利用する、という利用目的で使うことが多いです。

動的な HTML では基本的にハッシュを取ることが難しいため、
時間を扱う Last-Modified で管理したほうが実装がしやすいのです。

手順

  1. 最初のリクエスト時、HTMLレスポンスヘッダーに Last-Modified を設定して送信する
  2. 次回以降のリクエストヘッダに If-Modified-Since がついてくるので、読み取る
  3. 現在時刻と比較し、一定時間経過したか計測する
  • 一定時間経過していなければ、ステータスコード304でレスポンスする
  • 一定時間経過していれば、HTMLを生成して返す

サンプル

下記は、最初にページをロードした後10秒間だけHTMLをキャッシュさせる例です。

時刻のパースやフォーマットは癖がありますが、すべてのユーザーが10秒間キャッシュの恩恵を受けられます。

package main

import (
    "flag"
    "fmt"
    "io"
    "net/http"
    "os"
    "time"
)

var port = flag.String("http", ":8888", "HTTP port number.")

func init() {
    flag.Parse()

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

        sinceText := r.Header.Get("If-Modified-Since")
        if sinceText != "" {
            // JSTのまま time.Time にパースする
            loc, _ := time.LoadLocation("Asia/Tokyo")
            since, err := time.ParseInLocation("Mon, 02 Jan 2006 15:04:05 JST", sinceText, loc)

            // 10秒後の時間
            nowless10 := time.Now().Add(-10 * time.Second)

            // いろいろ表示
            fmt.Println()
            fmt.Println(since, err)
            fmt.Println(nowless10)
            fmt.Println(since.Before(nowless10))

            // 前回の記録から10秒以内であればキャッシュを読ませる
            if since.After(nowless10) {
                w.WriteHeader(http.StatusNotModified)
                return
            }
        }

        // HTMLの生成に3秒かかる想定
        time.Sleep(time.Second * 3)

        // Last-Modified を設定
        now := time.Now().Format("Mon, 02 Jan 2006 15:04:05 MST")
        w.Header().Set("Last-Modified", now)

        f, _ := os.Open("index.html")
        io.Copy(w, f)
    })
}

func main() {
    fmt.Printf("Start HTTP Server http://localhost%s/", *port)
    fmt.Println(http.ListenAndServe(*port, nil))
}

※ // いろいろ表示 というコメントの箇所は削除してもOKです。
※ 動作確認の際は適当に作った index.html を用意してください。

以上です。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1