在Golang中实现GitHub徽章

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

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 (
	&quot;log&quot;
	&quot;net/http&quot;
	_ &quot;time&quot;
)
var mymap map[string][]byte

var state bool = false


func viewHandler(w http.ResponseWriter, r *http.Request) {
	log.Printf(&quot;State %v&quot;, state)
	state = !state
	w.Header().Set(&quot;Content-Type&quot;, &quot;image/svg+xml&quot;)
	w.Header().Set(&quot;Cache-Control&quot;, &quot;no-cache, no-store, must-revalidate&quot;)
	if state {
		w.Write(mymap[&quot;correct&quot;])
	} else {
		w.Write(mymap[&quot;wrong&quot;])
	}
}

func main() {
	mymap = make(map[string][]byte)
	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;`)
	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;`)

	mux := http.NewServeMux()
	mux.HandleFunc(&quot;/view&quot;, viewHandler)
	http.ListenAndServe(&quot;:8085&quot;, 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:&quot;88e168c2d5cdb30ee9af739765e78e4d&quot;
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(&quot;Date&quot;, date)
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.)

package main

import (
	&quot;log&quot;
	&quot;net/http&quot;
	&quot;time&quot;
)

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(&quot;%v&quot;, date)
	log.Printf(&quot;State %v&quot;, state)
	state = !state
	w.Header().Set(&quot;Content-Type&quot;, &quot;image/svg+xml&quot;)
	w.Header().Set(&quot;Cache-Control&quot;, &quot;no-cache, no-store, must-revalidate&quot;)
	w.Header().Set(&quot;Date&quot;, date)
	w.Header().Set(&quot;Expires&quot;, date)
	if state {
		w.Write(mymap[&quot;correct&quot;])
	} else {
		w.Write(mymap[&quot;wrong&quot;])
	}
}

func main() {
	mymap = make(map[string][]byte)
	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;`)
	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;`)

	mux := http.NewServeMux()
	mux.HandleFunc(&quot;/view&quot;, viewHandler)
	log.Println(&quot;Server started. Listening on 8085...&quot;)
	http.ListenAndServe(&quot;:8085&quot;, mux)
}

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:

确定