はじめに
gin にはtemplateを使ったHTMLを返すメソッドは用意されていますが、
文字列をHTMLとして返すメソッドは用意されていません。
ですが
APIを作っていてどうしても1ページだけwebページを作りたくなる状況ってありますよね!?
つまり、わざわざtemplateをデプロイするのがめんどくさいに時間をかけたくない
バッドノウハウかもしれないけれど...
そもそもGinのレスポンスって何が返せるの
はい、というわけで、まずはGinのレスポンスで何を返せるのかを見て行きましょう
*READMEより抜粋しています
https://github.com/gin-gonic/gin/blob/master/context.go
JSON
言わずと知れたJSON
返せなかったら正気を疑う
c.JSON(http.StatusOK, msg)
XML
廃れつつあるXML。xmlのparseしなきゃいけない状況とか泣ける!
一昔前の議論
c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
YAML
rails界隈ではよく使われているみたいですけれど、今の所使う予定はなし。
https://qiita.com/Yama-to/items/587544993fb62610528a
c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
SecureJSON
調べるまで知らなかったけれど、
バグのせいで強制的にjsonを引っこ抜けることが昔あったらしく、その名残らしい。
IE6とか知らん(笑)
https://qiita.com/yuku_t/items/078eebf02bbbb45adc93
names := []string{"lena", "austin", "foo"}
// Will output : while(1);["lena","austin","foo"]
c.SecureJSON(http.StatusOK, names)
IndentedJSON
こちらも調べるまでしらなかったけれど、デバッグ時にはこれで出力するのも良いかも。
c.JSON(http.StatusOK, msg)
String
単に文字列を返したい時に使う。
test := "hoge"
c.String(200, "pong")
c.String(200, "%s", test)//みたいな書き方もできるらしい。
// 内部的にはfmt.Fprintf(w, format, data...)となるような使い方
// wはhttp.ResponseWriter
HTML
HTMLのレンダリング機能。ただしファイルからしか読み込めない。。。
もちろんファイルを置かなければpanicです
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"title": "Main website",
})
実装を眺める
ここでようやく本題です。結論から言えばcontext変数に値を入れてやれば終わりです。
contextを見て行きましょう。
// Context is the most important part of gin. It allows us to pass variables between middleware,
// manage the flow, validate the JSON of a request and render a JSON response for example.
type Context struct {
writermem responseWriter
Request *http.Request
Writer ResponseWriter
Params Params
handlers HandlersChain
index int8
engine *Engine
// Keys is a key/value pair exclusively for the context of each request.
Keys map[string]interface{}
// Errors is a list of errors attached to all the handlers/middlewares who used this context.
Errors errorMsgs
// Accepted defines a list of manually accepted formats for content negotiation.
Accepted []string
}
type ResponseWriter interface {
http.ResponseWriter
http.Hijacker
http.Flusher
http.CloseNotifier
// Returns the HTTP response status code of the current request.
Status() int
// Returns the number of bytes already written into the response http body.
// See Written()
Size() int
// Writes the string into the response body.
WriteString(string) (int, error)
// Returns true if the response body was already written.
Written() bool
// Forces to write the http header (status code + headers).
WriteHeaderNow()
}
func (w *responseWriter) WriteString(s string) (n int, err error) {
w.WriteHeaderNow()
n, err = io.WriteString(w.ResponseWriter, s)
w.size += n
return
}
よく見ると、
net/httpでレスポンスを書き込むを書き込むhttp.ResponseWriterが
Context.Writer.ResponseWiterにあるじゃないですか。
しかも御誂え向きのWriteStringが用意されてますね。
本題
というわけでコードを書いていきましょう
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.Writer.WriteString(`<!DOCTYPE html><html><body><input type="number"></body></html>`)
//c.String(200, `<!DOCTYPE html><html><body><input type="number"></body></html>`)
})
r.Run() // listen and serve on 0.0.0.0:8080
}
これを実行してhttp://127.0.0.1:8080/
にアクセスすると無事htmlとそのinput要素を表示することができました。
ちなみにコメントアウトしているc.Stringを実行すると文字列が虚しく表示されます。
まとめ
c.Writer.WriteString(``)を使えばなんとかなる。
バッドノウハウの可能性がありますので、用法用量を守ってお使いください。