在 Golang 中实现 GitHub 徽章
Implementing GitHub Badges in Golang
我已经问过这个问题before,但没有得到满意的答案,所以这次我会尝试更具体一些。
我想在golang中实现一个服务器,它以svg的形式输出动态状态更新。 (想想 "Build Passing/Failing" GitHub 徽章。)目的是应该能够将 link 嵌入 GitHub 自述文件中的服务器地址,自述文件应根据服务器状态。
这是我想出的 golang 代码,但它似乎不适用于 GitHub 积极缓存。我需要添加更多 Cache-Control headers 吗?我需要添加 ETag 吗?
我正在使用以下方法将图像嵌入 GitHub 自述文件。
[![Mine](http://58dcd0b5.ngrok.com/view)]()
理想情况下,我希望看到 GitHub 自述文件在我每次加载图像时更改图像 -- 在两个图像之间翻转 "correct"/"wrong"。 (这只是一个概念证明。)
package main
import (
"log"
"net/http"
_ "time"
)
var mymap map[string][]byte
var state bool = false
func viewHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("State %v", state)
state = !state
w.Header().Set("Content-Type", "image/svg+xml")
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
if state {
w.Write(mymap["correct"])
} else {
w.Write(mymap["wrong"])
}
}
func main() {
mymap = make(map[string][]byte)
mymap["correct"] = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="104" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="104" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#4c1" d="M54 0h50v20H54z"/><path fill="url(#b)" d="M0 0h104v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="28" y="15" fill="#010101" fill-opacity=".3">solution</text><text x="28" y="14">solution</text><text x="78" y="15" fill="#010101" fill-opacity=".3">correct</text><text x="78" y="14">correct</text></g></svg>`)
mymap["wrong"] = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="99" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="99" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#e05d44" d="M54 0h45v20H54z"/><path fill="url(#b)" d="M0 0h99v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="28" y="15" fill="#010101" fill-opacity=".3">solution</text><text x="28" y="14">solution</text><text x="75.5" y="15" fill="#010101" fill-opacity=".3">wrong</text><text x="75.5" y="14">wrong</text></g></svg>`)
mux := http.NewServeMux()
mux.HandleFunc("/view", viewHandler)
http.ListenAndServe(":8085", mux)
}
以下是 travis 为他们的图片提供的内容:
Age:0
Cache-Control:no-cache
Content-Length:0
Date:Mon, 30 Mar 2015 07:49:10 GMT
ETag:"88e168c2d5cdb30ee9af739765e78e4d"
Expires:Mon, 30 Mar 2015 07:49:10 GMT
Keep-Alive:timeout=10, max=48
Last-Modified:Wed, 07 Jan 2015 11:26:53 GMT
Timing-Allow-Origin:https://github.com
X-Timer:S1427701750.146025,VS0,VE156
尝试这些并看看效果如何可能是一个好的开始。
这是一个 Github 问题:https://github.com/github/markup/issues/224
Assets must include Cache-Control: no-cache and ETag headers. If a
badge is not updating, then it means they are not properly setting
these headers.
和
The assets may also need to include either an ETag or Expires header.
Fastly's docs on Cache-Control say that no-cache means "re-validate
before serving this content", but it doesn't specify what it does to
"re-validate". I'm guessing it is re-validating, but there's no
indication that the asset has changed, so it continues to serves the
cached asset.
An ETag would be the biggest win, since it would guarantee that the
cache gets refreshed when it changes, but still saves bandwidth.
在 this commit to shields.io server 之后,我对上面的代码进行了以下更改,现在可以使用了。
w.Header().Set("Date", date)
w.Header().Set("Expires", date)
为了完整性(以防万一有人想尝试一下),这里是完整的代码。 (也在 GitHub。)
package main
import (
"log"
"net/http"
"time"
)
var mymap map[string][]byte
var state bool = false
func viewHandler(w http.ResponseWriter, r *http.Request) {
date := time.Now().Format(http.TimeFormat)
log.Printf("%v", date)
log.Printf("State %v", state)
state = !state
w.Header().Set("Content-Type", "image/svg+xml")
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
w.Header().Set("Date", date)
w.Header().Set("Expires", date)
if state {
w.Write(mymap["correct"])
} else {
w.Write(mymap["wrong"])
}
}
func main() {
mymap = make(map[string][]byte)
mymap["correct"] = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="104" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="104" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#4c1" d="M54 0h50v20H54z"/><path fill="url(#b)" d="M0 0h104v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="28" y="15" fill="#010101" fill-opacity=".3">solution</text><text x="28" y="14">solution</text><text x="78" y="15" fill="#010101" fill-opacity=".3">correct</text><text x="78" y="14">correct</text></g></svg>`)
mymap["wrong"] = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="99" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="99" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#e05d44" d="M54 0h45v20H54z"/><path fill="url(#b)" d="M0 0h99v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="28" y="15" fill="#010101" fill-opacity=".3">solution</text><text x="28" y="14">solution</text><text x="75.5" y="15" fill="#010101" fill-opacity=".3">wrong</text><text x="75.5" y="14">wrong</text></g></svg>`)
mux := http.NewServeMux()
mux.HandleFunc("/view", viewHandler)
log.Println("Server started. Listening on 8085...")
http.ListenAndServe(":8085", mux)
}
我已经问过这个问题before,但没有得到满意的答案,所以这次我会尝试更具体一些。
我想在golang中实现一个服务器,它以svg的形式输出动态状态更新。 (想想 "Build Passing/Failing" GitHub 徽章。)目的是应该能够将 link 嵌入 GitHub 自述文件中的服务器地址,自述文件应根据服务器状态。
这是我想出的 golang 代码,但它似乎不适用于 GitHub 积极缓存。我需要添加更多 Cache-Control headers 吗?我需要添加 ETag 吗?
我正在使用以下方法将图像嵌入 GitHub 自述文件。
[![Mine](http://58dcd0b5.ngrok.com/view)]()
理想情况下,我希望看到 GitHub 自述文件在我每次加载图像时更改图像 -- 在两个图像之间翻转 "correct"/"wrong"。 (这只是一个概念证明。)
package main
import (
"log"
"net/http"
_ "time"
)
var mymap map[string][]byte
var state bool = false
func viewHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("State %v", state)
state = !state
w.Header().Set("Content-Type", "image/svg+xml")
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
if state {
w.Write(mymap["correct"])
} else {
w.Write(mymap["wrong"])
}
}
func main() {
mymap = make(map[string][]byte)
mymap["correct"] = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="104" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="104" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#4c1" d="M54 0h50v20H54z"/><path fill="url(#b)" d="M0 0h104v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="28" y="15" fill="#010101" fill-opacity=".3">solution</text><text x="28" y="14">solution</text><text x="78" y="15" fill="#010101" fill-opacity=".3">correct</text><text x="78" y="14">correct</text></g></svg>`)
mymap["wrong"] = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="99" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="99" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#e05d44" d="M54 0h45v20H54z"/><path fill="url(#b)" d="M0 0h99v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="28" y="15" fill="#010101" fill-opacity=".3">solution</text><text x="28" y="14">solution</text><text x="75.5" y="15" fill="#010101" fill-opacity=".3">wrong</text><text x="75.5" y="14">wrong</text></g></svg>`)
mux := http.NewServeMux()
mux.HandleFunc("/view", viewHandler)
http.ListenAndServe(":8085", mux)
}
以下是 travis 为他们的图片提供的内容:
Age:0
Cache-Control:no-cache
Content-Length:0
Date:Mon, 30 Mar 2015 07:49:10 GMT
ETag:"88e168c2d5cdb30ee9af739765e78e4d"
Expires:Mon, 30 Mar 2015 07:49:10 GMT
Keep-Alive:timeout=10, max=48
Last-Modified:Wed, 07 Jan 2015 11:26:53 GMT
Timing-Allow-Origin:https://github.com
X-Timer:S1427701750.146025,VS0,VE156
尝试这些并看看效果如何可能是一个好的开始。
这是一个 Github 问题:https://github.com/github/markup/issues/224
Assets must include Cache-Control: no-cache and ETag headers. If a badge is not updating, then it means they are not properly setting these headers.
和
The assets may also need to include either an ETag or Expires header. Fastly's docs on Cache-Control say that no-cache means "re-validate before serving this content", but it doesn't specify what it does to "re-validate". I'm guessing it is re-validating, but there's no indication that the asset has changed, so it continues to serves the cached asset.
An ETag would be the biggest win, since it would guarantee that the cache gets refreshed when it changes, but still saves bandwidth.
在 this commit to shields.io server 之后,我对上面的代码进行了以下更改,现在可以使用了。
w.Header().Set("Date", date)
w.Header().Set("Expires", date)
为了完整性(以防万一有人想尝试一下),这里是完整的代码。 (也在 GitHub。)
package main
import (
"log"
"net/http"
"time"
)
var mymap map[string][]byte
var state bool = false
func viewHandler(w http.ResponseWriter, r *http.Request) {
date := time.Now().Format(http.TimeFormat)
log.Printf("%v", date)
log.Printf("State %v", state)
state = !state
w.Header().Set("Content-Type", "image/svg+xml")
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
w.Header().Set("Date", date)
w.Header().Set("Expires", date)
if state {
w.Write(mymap["correct"])
} else {
w.Write(mymap["wrong"])
}
}
func main() {
mymap = make(map[string][]byte)
mymap["correct"] = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="104" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="104" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#4c1" d="M54 0h50v20H54z"/><path fill="url(#b)" d="M0 0h104v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="28" y="15" fill="#010101" fill-opacity=".3">solution</text><text x="28" y="14">solution</text><text x="78" y="15" fill="#010101" fill-opacity=".3">correct</text><text x="78" y="14">correct</text></g></svg>`)
mymap["wrong"] = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="99" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="99" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#e05d44" d="M54 0h45v20H54z"/><path fill="url(#b)" d="M0 0h99v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="28" y="15" fill="#010101" fill-opacity=".3">solution</text><text x="28" y="14">solution</text><text x="75.5" y="15" fill="#010101" fill-opacity=".3">wrong</text><text x="75.5" y="14">wrong</text></g></svg>`)
mux := http.NewServeMux()
mux.HandleFunc("/view", viewHandler)
log.Println("Server started. Listening on 8085...")
http.ListenAndServe(":8085", mux)
}