在Golang中实现GitHub徽章

huangapple go评论120阅读模式
英文:

Implementing GitHub Badges in Golang

问题

我之前提过这个问题,并没有得到满意的答案,所以这次我会更具体地描述。

我想在golang中实现一个服务器,以svg的形式输出动态的状态更新(类似于“构建通过/失败”的GitHub徽章)。目的是可以在GitHub的Readme中嵌入服务器地址的链接,并且根据服务器状态自动更新Readme。

以下是我编写的golang代码,但似乎无法与GitHub的缓存机制正常工作。我需要添加更多的Cache-Control头吗?我需要添加ETag吗?

我使用以下代码将图像嵌入GitHub的Readme中。

  1. [![Mine](http://58dcd0b5.ngrok.com/view)]()

理想情况下,我希望每次加载GitHub的Readme时,图像都会更改--在“正确”和“错误”两个图像之间切换。(这只是一个概念验证。)

  1. package main
  2. import (
  3. "log"
  4. "net/http"
  5. _ "time"
  6. )
  7. var mymap map[string][]byte
  8. var state bool = false
  9. func viewHandler(w http.ResponseWriter, r *http.Request) {
  10. log.Printf("State %v", state)
  11. state = !state
  12. w.Header().Set("Content-Type", "image/svg+xml")
  13. w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
  14. if state {
  15. w.Write(mymap["correct"])
  16. } else {
  17. w.Write(mymap["wrong"])
  18. }
  19. }
  20. func main() {
  21. mymap = make(map[string][]byte)
  22. 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>`)
  23. 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>`)
  24. mux := http.NewServeMux()
  25. mux.HandleFunc("/view", viewHandler)
  26. http.ListenAndServe(":8085", mux)
  27. }

希望能对你有所帮助!

英文:

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.

  1. [![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.)

  1. package main
  2. import (
  3. &quot;log&quot;
  4. &quot;net/http&quot;
  5. _ &quot;time&quot;
  6. )
  7. var mymap map[string][]byte
  8. var state bool = false
  9. func viewHandler(w http.ResponseWriter, r *http.Request) {
  10. log.Printf(&quot;State %v&quot;, state)
  11. state = !state
  12. w.Header().Set(&quot;Content-Type&quot;, &quot;image/svg+xml&quot;)
  13. w.Header().Set(&quot;Cache-Control&quot;, &quot;no-cache, no-store, must-revalidate&quot;)
  14. if state {
  15. w.Write(mymap[&quot;correct&quot;])
  16. } else {
  17. w.Write(mymap[&quot;wrong&quot;])
  18. }
  19. }
  20. func main() {
  21. mymap = make(map[string][]byte)
  22. mymap[&quot;correct&quot;] = []byte(`&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;104&quot; height=&quot;20&quot;&gt;&lt;linearGradient id=&quot;b&quot; x2=&quot;0&quot; y2=&quot;100%&quot;&gt;&lt;stop offset=&quot;0&quot; stop-color=&quot;#bbb&quot; stop-opacity=&quot;.1&quot;/&gt;&lt;stop offset=&quot;1&quot; stop-opacity=&quot;.1&quot;/&gt;&lt;/linearGradient&gt;&lt;mask id=&quot;a&quot;&gt;&lt;rect width=&quot;104&quot; height=&quot;20&quot; rx=&quot;3&quot; fill=&quot;#fff&quot;/&gt;&lt;/mask&gt;&lt;g mask=&quot;url(#a)&quot;&gt;&lt;path fill=&quot;#555&quot; d=&quot;M0 0h54v20H0z&quot;/&gt;&lt;path fill=&quot;#4c1&quot; d=&quot;M54 0h50v20H54z&quot;/&gt;&lt;path fill=&quot;url(#b)&quot; d=&quot;M0 0h104v20H0z&quot;/&gt;&lt;/g&gt;&lt;g fill=&quot;#fff&quot; text-anchor=&quot;middle&quot; font-family=&quot;DejaVu Sans,Verdana,Geneva,sans-serif&quot; font-size=&quot;11&quot;&gt;&lt;text x=&quot;28&quot; y=&quot;15&quot; fill=&quot;#010101&quot; fill-opacity=&quot;.3&quot;&gt;solution&lt;/text&gt;&lt;text x=&quot;28&quot; y=&quot;14&quot;&gt;solution&lt;/text&gt;&lt;text x=&quot;78&quot; y=&quot;15&quot; fill=&quot;#010101&quot; fill-opacity=&quot;.3&quot;&gt;correct&lt;/text&gt;&lt;text x=&quot;78&quot; y=&quot;14&quot;&gt;correct&lt;/text&gt;&lt;/g&gt;&lt;/svg&gt;`)
  23. mymap[&quot;wrong&quot;] = []byte(`&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;99&quot; height=&quot;20&quot;&gt;&lt;linearGradient id=&quot;b&quot; x2=&quot;0&quot; y2=&quot;100%&quot;&gt;&lt;stop offset=&quot;0&quot; stop-color=&quot;#bbb&quot; stop-opacity=&quot;.1&quot;/&gt;&lt;stop offset=&quot;1&quot; stop-opacity=&quot;.1&quot;/&gt;&lt;/linearGradient&gt;&lt;mask id=&quot;a&quot;&gt;&lt;rect width=&quot;99&quot; height=&quot;20&quot; rx=&quot;3&quot; fill=&quot;#fff&quot;/&gt;&lt;/mask&gt;&lt;g mask=&quot;url(#a)&quot;&gt;&lt;path fill=&quot;#555&quot; d=&quot;M0 0h54v20H0z&quot;/&gt;&lt;path fill=&quot;#e05d44&quot; d=&quot;M54 0h45v20H54z&quot;/&gt;&lt;path fill=&quot;url(#b)&quot; d=&quot;M0 0h99v20H0z&quot;/&gt;&lt;/g&gt;&lt;g fill=&quot;#fff&quot; text-anchor=&quot;middle&quot; font-family=&quot;DejaVu Sans,Verdana,Geneva,sans-serif&quot; font-size=&quot;11&quot;&gt;&lt;text x=&quot;28&quot; y=&quot;15&quot; fill=&quot;#010101&quot; fill-opacity=&quot;.3&quot;&gt;solution&lt;/text&gt;&lt;text x=&quot;28&quot; y=&quot;14&quot;&gt;solution&lt;/text&gt;&lt;text x=&quot;75.5&quot; y=&quot;15&quot; fill=&quot;#010101&quot; fill-opacity=&quot;.3&quot;&gt;wrong&lt;/text&gt;&lt;text x=&quot;75.5&quot; y=&quot;14&quot;&gt;wrong&lt;/text&gt;&lt;/g&gt;&lt;/svg&gt;`)
  24. mux := http.NewServeMux()
  25. mux.HandleFunc(&quot;/view&quot;, viewHandler)
  26. http.ListenAndServe(&quot;:8085&quot;, mux)
  27. }

答案1

得分: 4

以下是Travis为其图像提供的内容:

  1. 年龄:0
  2. 缓存控制:无缓存
  3. 内容长度:0
  4. 日期:2015330日星期一07:49:10 GMT
  5. ETag"88e168c2d5cdb30ee9af739765e78e4d"
  6. 过期时间:2015330日星期一07:49:10 GMT
  7. 保持连接时间:超时=10,最大=48
  8. 上次修改时间:201517日星期三11:26:53 GMT
  9. 计时允许来源:https://github.com
  10. X计时器:S1427701750.146025VS0VE156

尝试使用这些内容可能是一个不错的开始,看看哪些有效。

英文:

Here's what travis are serving for their images:

  1. Age:0
  2. Cache-Control:no-cache
  3. Content-Length:0
  4. Date:Mon, 30 Mar 2015 07:49:10 GMT
  5. ETag:&quot;88e168c2d5cdb30ee9af739765e78e4d&quot;
  6. Expires:Mon, 30 Mar 2015 07:49:10 GMT
  7. Keep-Alive:timeout=10, max=48
  8. Last-Modified:Wed, 07 Jan 2015 11:26:53 GMT
  9. Timing-Allow-Origin:https://github.com
  10. 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服务器,我对上面的代码进行了以下更改,现在它可以工作了。

  1. w.Header().Set("Date", date)
  2. w.Header().Set("Expires", date)

为了完整起见(以防有人想尝试),这是完整的代码(也可以在GitHub上找到)。

  1. package main
  2. import (
  3. "log"
  4. "net/http"
  5. "time"
  6. )
  7. var mymap map[string][]byte
  8. var state bool = false
  9. func viewHandler(w http.ResponseWriter, r *http.Request) {
  10. date := time.Now().Format(http.TimeFormat)
  11. log.Printf("%v", date)
  12. log.Printf("State %v", state)
  13. state = !state
  14. w.Header().Set("Content-Type", "image/svg+xml")
  15. w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
  16. w.Header().Set("Date", date)
  17. w.Header().Set("Expires", date)
  18. if state {
  19. w.Write(mymap["correct"])
  20. } else {
  21. w.Write(mymap["wrong"])
  22. }
  23. }
  24. func main() {
  25. mymap = make(map[string][]byte)
  26. 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>`)
  27. 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>`)
  28. mux := http.NewServeMux()
  29. mux.HandleFunc("/view", viewHandler)
  30. log.Println("Server started. Listening on 8085...")
  31. http.ListenAndServe(":8085", mux)
  32. }
英文:

Following this commit to shields.io server, I made the following changes to the above code and it works now.

  1. w.Header().Set(&quot;Date&quot;, date)
  2. w.Header().Set(&quot;Expires&quot;, date)

For completeness (and in case someone wants to try it out), here's the complete code. (Also on GitHub.)

  1. package main
  2. import (
  3. &quot;log&quot;
  4. &quot;net/http&quot;
  5. &quot;time&quot;
  6. )
  7. var mymap map[string][]byte
  8. var state bool = false
  9. func viewHandler(w http.ResponseWriter, r *http.Request) {
  10. date := time.Now().Format(http.TimeFormat)
  11. log.Printf(&quot;%v&quot;, date)
  12. log.Printf(&quot;State %v&quot;, state)
  13. state = !state
  14. w.Header().Set(&quot;Content-Type&quot;, &quot;image/svg+xml&quot;)
  15. w.Header().Set(&quot;Cache-Control&quot;, &quot;no-cache, no-store, must-revalidate&quot;)
  16. w.Header().Set(&quot;Date&quot;, date)
  17. w.Header().Set(&quot;Expires&quot;, date)
  18. if state {
  19. w.Write(mymap[&quot;correct&quot;])
  20. } else {
  21. w.Write(mymap[&quot;wrong&quot;])
  22. }
  23. }
  24. func main() {
  25. mymap = make(map[string][]byte)
  26. mymap[&quot;correct&quot;] = []byte(`&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;104&quot; height=&quot;20&quot;&gt;&lt;linearGradient id=&quot;b&quot; x2=&quot;0&quot; y2=&quot;100%&quot;&gt;&lt;stop offset=&quot;0&quot; stop-color=&quot;#bbb&quot; stop-opacity=&quot;.1&quot;/&gt;&lt;stop offset=&quot;1&quot; stop-opacity=&quot;.1&quot;/&gt;&lt;/linearGradient&gt;&lt;mask id=&quot;a&quot;&gt;&lt;rect width=&quot;104&quot; height=&quot;20&quot; rx=&quot;3&quot; fill=&quot;#fff&quot;/&gt;&lt;/mask&gt;&lt;g mask=&quot;url(#a)&quot;&gt;&lt;path fill=&quot;#555&quot; d=&quot;M0 0h54v20H0z&quot;/&gt;&lt;path fill=&quot;#4c1&quot; d=&quot;M54 0h50v20H54z&quot;/&gt;&lt;path fill=&quot;url(#b)&quot; d=&quot;M0 0h104v20H0z&quot;/&gt;&lt;/g&gt;&lt;g fill=&quot;#fff&quot; text-anchor=&quot;middle&quot; font-family=&quot;DejaVu Sans,Verdana,Geneva,sans-serif&quot; font-size=&quot;11&quot;&gt;&lt;text x=&quot;28&quot; y=&quot;15&quot; fill=&quot;#010101&quot; fill-opacity=&quot;.3&quot;&gt;solution&lt;/text&gt;&lt;text x=&quot;28&quot; y=&quot;14&quot;&gt;solution&lt;/text&gt;&lt;text x=&quot;78&quot; y=&quot;15&quot; fill=&quot;#010101&quot; fill-opacity=&quot;.3&quot;&gt;correct&lt;/text&gt;&lt;text x=&quot;78&quot; y=&quot;14&quot;&gt;correct&lt;/text&gt;&lt;/g&gt;&lt;/svg&gt;`)
  27. mymap[&quot;wrong&quot;] = []byte(`&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;99&quot; height=&quot;20&quot;&gt;&lt;linearGradient id=&quot;b&quot; x2=&quot;0&quot; y2=&quot;100%&quot;&gt;&lt;stop offset=&quot;0&quot; stop-color=&quot;#bbb&quot; stop-opacity=&quot;.1&quot;/&gt;&lt;stop offset=&quot;1&quot; stop-opacity=&quot;.1&quot;/&gt;&lt;/linearGradient&gt;&lt;mask id=&quot;a&quot;&gt;&lt;rect width=&quot;99&quot; height=&quot;20&quot; rx=&quot;3&quot; fill=&quot;#fff&quot;/&gt;&lt;/mask&gt;&lt;g mask=&quot;url(#a)&quot;&gt;&lt;path fill=&quot;#555&quot; d=&quot;M0 0h54v20H0z&quot;/&gt;&lt;path fill=&quot;#e05d44&quot; d=&quot;M54 0h45v20H54z&quot;/&gt;&lt;path fill=&quot;url(#b)&quot; d=&quot;M0 0h99v20H0z&quot;/&gt;&lt;/g&gt;&lt;g fill=&quot;#fff&quot; text-anchor=&quot;middle&quot; font-family=&quot;DejaVu Sans,Verdana,Geneva,sans-serif&quot; font-size=&quot;11&quot;&gt;&lt;text x=&quot;28&quot; y=&quot;15&quot; fill=&quot;#010101&quot; fill-opacity=&quot;.3&quot;&gt;solution&lt;/text&gt;&lt;text x=&quot;28&quot; y=&quot;14&quot;&gt;solution&lt;/text&gt;&lt;text x=&quot;75.5&quot; y=&quot;15&quot; fill=&quot;#010101&quot; fill-opacity=&quot;.3&quot;&gt;wrong&lt;/text&gt;&lt;text x=&quot;75.5&quot; y=&quot;14&quot;&gt;wrong&lt;/text&gt;&lt;/g&gt;&lt;/svg&gt;`)
  28. mux := http.NewServeMux()
  29. mux.HandleFunc(&quot;/view&quot;, viewHandler)
  30. log.Println(&quot;Server started. Listening on 8085...&quot;)
  31. http.ListenAndServe(&quot;:8085&quot;, mux)
  32. }

huangapple
  • 本文由 发表于 2015年3月30日 15:42:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/29340796.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定