Go言語におけるWebフレームワークのコンテキスト設計

コンテキストの実装と応用

Webフレームワーク開発において、コンテキスト(Context)の設計は重要な要素です。本稿では、Go言語を使用して効率的なコンテキスト構造を実装する方法について解説します。

実装例

以下は、実際の使用例を示します。

func main() {
    app := web.New()
    app.GET("/", func(ctx *web.Context) {
        ctx.HTML(http.StatusOK, "<h1>ウェルカム</h1>")
    })
    app.GET("/greet", func(ctx *web.Context) {
        ctx.String(http.StatusOK, "こんにちは %s, アクセスパス: %s\n", 
            ctx.Query("user"), ctx.Path)
    })

    app.POST("/auth", func(ctx *web.Context) {
        ctx.JSON(http.StatusOK, web.DataMap{
            "user_id": ctx.PostForm("user_id"),
            "pass":    ctx.PostForm("password"),
        })
    })

    app.Start(":8080")
}

コンテキスト設計の重要性

コンテキストはHTTPリクエストとレスポンスを抽象化し、開発者が繰り返しのコードを書く必要を減らします。特にJSONレスポンスの生成において、その効果が顕著です。

従来の実装方法:

data := map[string]interface{}{
    "user": "yamada",
    "role": "admin",
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
encoder := json.NewEncoder(w)
if err := encoder.Encode(data); err != nil {
    http.Error(w, err.Error(), 500)
}

コンテキストを使用した実装:

ctx.JSON(http.StatusOK, web.DataMap{
    "user": ctx.PostForm("username"),
    "role": ctx.PostForm("role"),
})

コンテキスト構造の実装

type DataMap map[string]interface{}

type Context struct {
    Writer     http.ResponseWriter
    Request    *http.Request
    Path       string
    Method     string
    StatusCode int
}

func createContext(w http.ResponseWriter, req *http.Request) *Context {
    return &Context{
        Writer:  w,
        Request: req,
        Path:    req.URL.Path,
        Method:  req.Method,
    }
}

func (ctx *Context) GetFormValue(key string) string {
    return ctx.Request.FormValue(key)
}

func (ctx *Context) GetQuery(key string) string {
    return ctx.Request.URL.Query().Get(key)
}

func (ctx *Context) SetStatus(code int) {
    ctx.StatusCode = code
    ctx.Writer.WriteHeader(code)
}

func (ctx *Context) SetResponseHeader(key, value string) {
    ctx.Writer.Header().Set(key, value)
}

func (ctx *Context) TextResponse(code int, format string, args ...interface{}) {
    ctx.SetResponseHeader("Content-Type", "text/plain")
    ctx.SetStatus(code)
    ctx.Writer.Write([]byte(fmt.Sprintf(format, args...)))
}

func (ctx *Context) JSONResponse(code int, data interface{}) {
    ctx.SetResponseHeader("Content-Type", "application/json")
    ctx.SetStatus(code)
    encoder := json.NewEncoder(ctx.Writer)
    if err := encoder.Encode(data); err != nil {
        http.Error(ctx.Writer, err.Error(), 500)
    }
}

func (ctx *Context) BinaryResponse(code int, content []byte) {
    ctx.SetStatus(code)
    ctx.Writer.Write(content)
}

func (ctx *Context) HTMLResponse(code int, content string) {
    ctx.SetResponseHeader("Content-Type", "text/html")
    ctx.SetStatus(code)
    ctx.Writer.Write([]byte(content))
}

ルーティングシステムの分離

ルーティング機能を独立させることで、システムの拡張性が向上します。

type routeHandler func(*Context)

type router struct {
    routes map[string]routeHandler
}

func initializeRouter() *router {
    return &router{routes: make(map[string]routeHandler)}
}

func (r *router) registerRoute(method, path string, handler routeHandler) {
    log.Printf("登録ルート %s - %s", method, path)
    routeKey := method + "-" + path
    r.routes[routeKey] = handler
}

func (r *router) processRequest(ctx *Context) {
    routeKey := ctx.Method + "-" + ctx.Path
    if handler, exists := r.routes[routeKey]; exists {
        handler(ctx)
    } else {
        ctx.TextResponse(http.StatusNotFound, "404 ページが見つかりません: %s\n", ctx.Path)
    }
}

フレームワークコアの実装

type WebApp struct {
    router *router
}

func Initialize() *WebApp {
    return &WebApp{router: initializeRouter()}
}

func (app *WebApp) register(method, path string, handler routeHandler) {
    app.router.registerRoute(method, path, handler)
}

func (app *WebApp) GET(path string, handler routeHandler) {
    app.register("GET", path, handler)
}

func (app *WebApp) POST(path string, handler routeHandler) {
    app.register("POST", path, handler)
}

func (app *WebApp) Start(address string) error {
    return http.ListenAndServe(address, app)
}

func (app *WebApp) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    ctx := createContext(w, req)
    app.router.processRequest(ctx)
}

この実装により、HTTPリクエストの処理が効率化され、開発者はビジネスロジックに集中できるようになります。

タグ: Go Webフレームワーク Context HTTPハンドリング ルーティング

5月19日 07:56 投稿