英文:
Implementing GitHub Badges in Golang
问题
我之前提过这个问题,并没有得到满意的答案,所以这次我会更具体地描述。
我想在golang中实现一个服务器,以svg的形式输出动态的状态更新(类似于“构建通过/失败”的GitHub徽章)。目的是可以在GitHub的Readme中嵌入服务器地址的链接,并且根据服务器状态自动更新Readme。
以下是我编写的golang代码,但似乎无法与GitHub的缓存机制正常工作。我需要添加更多的Cache-Control头吗?我需要添加ETag吗?
我使用以下代码将图像嵌入GitHub的Readme中。
[![Mine](http://58dcd0b5.ngrok.com/view)]()
理想情况下,我希望每次加载GitHub的Readme时,图像都会更改--在“正确”和“错误”两个图像之间切换。(这只是一个概念验证。)
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)
}
希望能对你有所帮助!
英文:
I have asked this question before and didn't receive satisfactory answer, so this time I'd try to be more specific.
I would like to implement a server in golang which outputs dynamic status updates in the form of svg. (Think "Build Passing/Failing" GitHub Badges.) The purpose is that one should be able to embed a link to the server's address in GitHub Readme and the Readme should update automatically depending on the server state.
Here's the golang code that I came up with but it doesn't seem to work with GitHub aggressive caching. Do I need to add more Cache-Control headers? Do I need to add ETag?
I'm using the following to embed the image in GitHub Readme.
[![Mine](http://58dcd0b5.ngrok.com/view)]()
Ideally, I would like to see the GitHub Readme change the image every time I load it -- flipping between the two images "correct"/"wrong". (This is just a proof of concept.)
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)
}
答案1
得分: 4
以下是Travis为其图像提供的内容:
年龄:0
缓存控制:无缓存
内容长度:0
日期:2015年3月30日星期一07:49:10 GMT
ETag:"88e168c2d5cdb30ee9af739765e78e4d"
过期时间:2015年3月30日星期一07:49:10 GMT
保持连接时间:超时=10,最大=48
上次修改时间:2015年1月7日星期三11:26:53 GMT
计时允许来源:https://github.com
X计时器:S1427701750.146025,VS0,VE156
尝试使用这些内容可能是一个不错的开始,看看哪些有效。
英文:
Here's what travis are serving for their images:
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
It might be a good start to try these and see what works.
答案2
得分: 4
这是一个关于此问题的Github问题:https://github.com/github/markup/issues/224
资源必须包含Cache-Control: no-cache和ETag头。如果徽章没有更新,那么意味着它们没有正确设置这些头部。
和
资源还可能需要包含ETag或Expires头。Fastly关于Cache-Control的文档中说,no-cache表示“在提供此内容之前重新验证”,但它没有指定“重新验证”的具体操作。我猜测它是在重新验证,但没有表明资源是否已更改,因此它继续提供缓存的资源。
ETag将是最大的优势,因为它可以确保在更改时刷新缓存,同时节省带宽。
英文:
Here is a Github issue about it : 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.
and
> 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.
答案3
得分: 1
根据这个提交到shields.io服务器,我对上面的代码进行了以下更改,现在它可以工作了。
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)
}
英文:
Following this commit to shields.io server, I made the following changes to the above code and it works now.
w.Header().Set("Date", date)
w.Header().Set("Expires", date)
For completeness (and in case someone wants to try it out), here's the complete code. (Also on 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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论