LoginSignup
7
7

More than 5 years have passed since last update.

【Go】Gin でテンプレートファイルを使わずにhtmlを返したい(レスポンスメソッドのおさらいを兼ねて)

Posted at

はじめに

gin にはtemplateを使ったHTMLを返すメソッドは用意されていますが、
文字列をHTMLとして返すメソッドは用意されていません。

ですが
APIを作っていてどうしても1ページだけwebページを作りたくなる状況ってありますよね!?

つまり、わざわざtemplateをデプロイするのがめんどくさいに時間をかけたくない
バッドノウハウかもしれないけれど...

そもそもGinのレスポンスって何が返せるの

はい、というわけで、まずはGinのレスポンスで何を返せるのかを見て行きましょう
*READMEより抜粋しています
https://github.com/gin-gonic/gin/blob/master/context.go

JSON

言わずと知れたJSON
返せなかったら正気を疑う

JSON
c.JSON(http.StatusOK, msg)

XML

廃れつつあるXML。xmlのparseしなきゃいけない状況とか泣ける!
一昔前の議論

XML
c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})

YAML

rails界隈ではよく使われているみたいですけれど、今の所使う予定はなし。
https://qiita.com/Yama-to/items/587544993fb62610528a

YAML
c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})

SecureJSON

調べるまで知らなかったけれど、
バグのせいで強制的にjsonを引っこ抜けることが昔あったらしく、その名残らしい。
IE6とか知らん(笑)
https://qiita.com/yuku_t/items/078eebf02bbbb45adc93

SecureJson
    names := []string{"lena", "austin", "foo"}
    // Will output  :   while(1);["lena","austin","foo"]
    c.SecureJSON(http.StatusOK, names)

IndentedJSON

こちらも調べるまでしらなかったけれど、デバッグ時にはこれで出力するのも良いかも。

IndentedJSON
c.JSON(http.StatusOK, msg)

String

単に文字列を返したい時に使う。

String
test := "hoge"
c.String(200, "pong")
c.String(200, "%s", test)//みたいな書き方もできるらしい。
// 内部的にはfmt.Fprintf(w, format, data...)となるような使い方
// wはhttp.ResponseWriter

HTML

HTMLのレンダリング機能。ただしファイルからしか読み込めない。。。
もちろんファイルを置かなければpanicです

HTML
c.HTML(http.StatusOK, "index.tmpl", gin.H{
    "title": "Main website",
})

実装を眺める

ここでようやく本題です。結論から言えばcontext変数に値を入れてやれば終わりです。
contextを見て行きましょう。

context.go

// 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
}
response_writer.go
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が用意されてますね。

本題

というわけでコードを書いていきましょう

main.go
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(``)を使えばなんとかなる。
バッドノウハウの可能性がありますので、用法用量を守ってお使いください。

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7