Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Building a Basic Web Framework with Go's net/http Package

Tech 2

Go's standard library includes net/http, which provides foundational tools for HTTP programming. This framework, named Gee, builds upon net/http to create a simple web framework. Below is an example demonstrating basic usage of the standard library to start a web server.

Example: Standard Library Server Setup

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", pathHandler)
    http.HandleFunc("/greet", headerHandler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func pathHandler(resp http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(resp, "Path: %s\n", req.URL.Path)
}

func headerHandler(resp http.ResponseWriter, req *http.Request) {
    for key, values := range req.Header {
        fmt.Fprintf(resp, "%s: %v\n", key, values)
    }
}

This code sets up two routes: / and /greet, each linked to a handler function. The server listens on port 8080. Testing with a tool like curl yields responses based on the request path and headers.

To customize request handling, implement the http.Handler enterface, which requires a ServeHTTP method. This allows intercepting all HTTP requests through a single instance.

Example: Custom Handler Implementation

package main

import (
    "fmt"
    "log"
    "net/http"
)

type Server struct{}

func (s *Server) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
    switch req.URL.Path {
    case "/":
        fmt.Fprintf(resp, "Path: %s\n", req.URL.Path)
    case "/greet":
        for key, values := range req.Header {
            fmt.Fprintf(resp, "%s: %v\n", key, values)
        }
    default:
        resp.WriteHeader(http.StatusNotFound)
        fmt.Fprintf(resp, "404: %s\n", req.URL)
    }
}

func main() {
    server := &Server{}
    log.Fatal(http.ListenAndServe(":8080", server))
}

By passing a Server instance to ListenAndServe, all requests are directed to ServeHTTP, enabling centralized control for routing and logic like error handling.

Next, structure this into a basic framework. The project layout includes:

  • gee/gee.go
  • main.go
  • go.mod files

Main Application File

package main

import (
    "fmt"
    "net/http"
    "gee"
)

func main() {
    app := gee.Create()
    app.GET("/", func(resp http.ResponseWriter, req *http.Request) {
        fmt.Fprintf(resp, "Path: %s\n", req.URL.Path)
    })
    app.GET("/greet", func(resp http.ResponseWriter, req *http.Request) {
        for key, values := range req.Header {
            fmt.Fprintf(resp, "%s: %v\n", key, values)
        }
    })
    app.Start(":8080")
}

This mirrors frameworks like Gin, using methods to add routes and start the server. Currently, it supports static routes only.

Framework Core Implemantation

package gee

import (
    "fmt"
    "net/http"
)

type RouteHandler func(http.ResponseWriter, *http.Request)

type App struct {
    routes map[string]RouteHandler
}

func Create() *App {
    return &App{routes: make(map[string]RouteHandler)}
}

func (a *App) registerRoute(method, path string, handler RouteHandler) {
    key := method + "-" + path
    a.routes[key] = handler
}

func (a *App) GET(path string, handler RouteHandler) {
    a.registerRoute("GET", path, handler)
}

func (a *App) POST(path string, handler RouteHandler) {
    a.registerRoute("POST", path, handler)
}

func (a *App) Start(address string) error {
    return http.ListenAndServe(address, a)
}

func (a *App) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
    routeKey := req.Method + "-" + req.URL.Path
    if handler, exists := a.routes[routeKey]; exists {
        handler(resp, req)
    } else {
        resp.WriteHeader(http.StatusNotFound)
        fmt.Fprintf(resp, "404: %s\n", req.URL)
    }
}

In this implementation, RouteHandler defines user-provided functions for routes. The App struct maintains a routing map where keys combine HTTP methods and paths. Methods like GET and POST register handlers, and ServeHTTP looks up and executes them or returns a 404 error.

Running this setup with go run main.go and testing with curl confirms it handles requests as expected, providing a foundation for adding features like dynamic routing in future iterations.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.