framework

package module
v0.0.0-...-eadd4a4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 29, 2026 License: MIT Imports: 16 Imported by: 0

README

🕸️ Simple Web Framework

A lightweight HTTP framework built on top of Go's standard library, demonstrating how to implement an extensible web framework using IoC Container, Plugin System, and Codec Registry design patterns.

go get github.com/xchwan/simple-web-framework

🚀 Quick Start

package main

import (
    "net/http"

    framework "github.com/xchwan/simple-web-framework"
)

func main() {
    r := framework.NewRouter()

    r.GET("/hello", func(w http.ResponseWriter, r *http.Request) {
        framework.Respond(w, r, http.StatusOK, map[string]string{"message": "Hello, World!"})
    })

    r.Run(":8080")
}

📚 Documentation

Topic Description
🗺️ Routing Path params, query strings, HTTP methods, route grouping, Routes interface
📥 Request Parsing ParseRequest (manual) and ParseOrRespond (auto)
📤 Response Serialization Content-negotiation, error body format
🚨 Error Handling HandleError, ErrBadRequest, ExceptionMapperPlugin
🧩 IoC Container Bind, Resolve, Get[T], lifecycle scopes
🔌 Plugin System Installer, RouteHook, ContextInjector, PluginContext
🗜️ Codec Extension XML support, custom media types
🔗 Middleware Chain Decorator pattern, pre/post handler logic
🪝 Hook System OnRequest, OnRespond, OnError observers
🛑 Graceful Shutdown SIGINT / SIGTERM handling, drain in-flight requests
📖 API Documentation Swagger UI, OpenAPI 3.0, DocPlugin, Doc[Req, Resp]
📦 Full Example End-to-end user service wiring
🎨 Design Patterns Factory Method, Decorator, Chain of Responsibility, …

🛠️ Development Commands

All commands run inside Docker — no local Go installation required.

make all          # ✅ staticcheck + format + test + build (full CI pipeline)
make test         # 🧪 Run integration tests under ./test/...
make build        # 🏗️ Compile binary
make staticcheck  # 🔍 Static analysis
make format       # 🎨 gofmt
make tidy         # 📦 go mod tidy
make shell        # 🐚 Interactive container shell
make clean        # 🧹 Remove binary and build cache

👤 Author

xchwan


Contributions are welcome! The .claude/ directory and CLAUDE.md are checked in to help contributors get started with Claude Code without additional setup.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrBadRequest = errors.New("bad request")

ErrBadRequest is a framework-level sentinel representing a malformed request (e.g. JSON parse failure). HandleError maps it to 400 automatically — no ExceptionMapperPlugin configuration required.

Functions

func Get

func Get[T any](r *http.Request, name string) T

Get retrieves a named dependency from the container stored in the request context.

func HandleError

func HandleError(w http.ResponseWriter, r *http.Request, err error)

HandleError converts a Go error into an HTTP response, trying in order:

  1. ExceptionMapperPlugin custom rules
  2. Framework defaults (ErrBadRequest → 400)
  3. Fallback 500

func ParseOrRespond

func ParseOrRespond(w http.ResponseWriter, r *http.Request, v any) error

ParseOrRespond behaves like ParseRequest but automatically calls HandleError on decode failure. HandleError tries ExceptionMapperPlugin rules → ErrBadRequest default (400) → fallback 500. The caller only needs to check whether the returned error is nil and return early if not.

func ParseRequest

func ParseRequest(r *http.Request, v any) error

ParseRequest selects a Codec based on the Content-Type header and decodes the request body into v. On failure the raw error is returned and the caller is responsible for responding.

func PathParam

func PathParam(r *http.Request, key string) string

PathParam retrieves a named path parameter from the request context.

func Respond

func Respond(w http.ResponseWriter, r *http.Request, statusCode int, body any)

Respond selects a Codec based on the Content-Type header, serializes body, and writes the response. For 204 No Content the Content-Type header is omitted and no body is written. Routing-layer 404/405 errors bypass this function and go directly through the error handler.

Types

type Container

type Container struct {
	// contains filtered or unexported fields
}

Container manages dependency registrations and their lifecycles.

func NewContainer

func NewContainer() *Container

NewContainer creates an empty Container.

func (*Container) Register

func (c *Container) Register(name string, factory func() any, s ...scope.Scope)

Register adds a dependency to the container. Defaults to SingletonScope when no scope is provided.

func (*Container) Resolve

func (c *Container) Resolve(ctx context.Context, name string) any

Resolve retrieves the named dependency, delegating creation to the registered scope.

type ErrorBody

type ErrorBody struct {
	Message string `json:"message"`
}

ErrorBody is the unified JSON error response structure.

func Error

func Error(message string) ErrorBody

Error constructs an ErrorBody with the given message.

type ErrorHandlerFunc

type ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, statusCode int)

ErrorHandlerFunc handles routing-layer HTTP errors (404, 405).

type Group

type Group struct {
	// contains filtered or unexported fields
}

Group is a route builder that prepends a common path prefix and a set of shared middlewares to every route registered through it. Create one with Router.Group; the underlying Router is unaware of groups — each call to g.GET / g.POST / … expands directly into a top-level route.

func (*Group) DELETE

func (g *Group) DELETE(path string, f HandlerFunc, m ...MiddlewareFunc)

func (*Group) GET

func (g *Group) GET(path string, f HandlerFunc, m ...MiddlewareFunc)

func (*Group) Group

func (g *Group) Group(prefix string, m ...MiddlewareFunc) *Group

Group creates a nested group that inherits this group's prefix and middlewares, then appends the given prefix and middlewares on top.

func (*Group) HandleFunc

func (g *Group) HandleFunc(method, path string, f HandlerFunc, m ...MiddlewareFunc)

HandleFunc registers a handler for an arbitrary HTTP method. Useful when the five convenience methods (GET/POST/PUT/PATCH/DELETE) are not enough.

func (*Group) PATCH

func (g *Group) PATCH(path string, f HandlerFunc, m ...MiddlewareFunc)

func (*Group) POST

func (g *Group) POST(path string, f HandlerFunc, m ...MiddlewareFunc)

func (*Group) PUT

func (g *Group) PUT(path string, f HandlerFunc, m ...MiddlewareFunc)

type HandlerFunc

type HandlerFunc = routing.HandlerFunc

HandlerFunc is a type alias for routing.HandlerFunc so callers do not need to import the routing package directly.

type MiddlewareFunc

type MiddlewareFunc = routing.MiddlewareFunc

MiddlewareFunc is a type alias for routing.MiddlewareFunc so callers do not need to import the routing package directly.

type Router

type Router struct {
	// contains filtered or unexported fields
}

Router holds a set of HttpHandlers and tries each one in order for every incoming request.

func NewRouter

func NewRouter() *Router

NewRouter creates and returns a new Router with the default error handler and built-in JSON and text/plain codecs pre-registered.

func (*Router) AddPlugin

func (ro *Router) AddPlugin(p any)

AddPlugin installs a plugin, storing it in the plugins map keyed by its type. If p implements Installer, Install is called immediately with the current PluginContext. If p implements ContextInjector, Inject is called automatically on every request.

func (*Router) Bind

func (ro *Router) Bind(name string, factory func() any, s ...scope.Scope)

Bind registers a dependency with the container. Defaults to SingletonScope when no scope is provided.

func (*Router) DELETE

func (ro *Router) DELETE(path string, f HandlerFunc, m ...MiddlewareFunc)

func (*Router) GET

func (ro *Router) GET(path string, f HandlerFunc, m ...MiddlewareFunc)

func (*Router) Group

func (ro *Router) Group(prefix string, m ...MiddlewareFunc) *Group

Group returns a new Group whose routes share the given path prefix and middlewares. The middlewares are prepended to any per-route middlewares, so execution order is: group middlewares → route middlewares → handler.

func (*Router) OnError

func (ro *Router) OnError(f hook.OnErrorFunc)

OnError registers a hook that fires when HandleError writes an error response.

func (*Router) OnRequest

func (ro *Router) OnRequest(f hook.OnRequestFunc)

OnRequest registers a hook that fires on every incoming request, before dispatch.

func (*Router) OnRespond

func (ro *Router) OnRespond(f hook.OnRespondFunc)

OnRespond registers a hook that fires when Respond writes a successful response.

func (*Router) PATCH

func (ro *Router) PATCH(path string, f HandlerFunc, m ...MiddlewareFunc)

func (*Router) POST

func (ro *Router) POST(path string, f HandlerFunc, m ...MiddlewareFunc)

func (*Router) PUT

func (ro *Router) PUT(path string, f HandlerFunc, m ...MiddlewareFunc)

func (*Router) Run

func (ro *Router) Run(addr string) error

Run starts the HTTP server and listens on the given address (e.g. ":8080"). It blocks until SIGINT or SIGTERM is received, then gracefully shuts down: new requests are rejected and in-flight requests are given up to SetShutdownTimeout (default 5s) to complete before the server exits.

func (*Router) ServeHTTP

func (ro *Router) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler and is the single entry point for every HTTP request.

func (*Router) SetErrorHandler

func (ro *Router) SetErrorHandler(f ErrorHandlerFunc)

SetErrorHandler overrides the default routing-error handler (404 / 405).

func (*Router) SetShutdownTimeout

func (ro *Router) SetShutdownTimeout(d time.Duration)

SetShutdownTimeout sets the maximum time Run will wait for in-flight requests to finish after a SIGINT or SIGTERM is received. Defaults to 5 seconds.

Directories

Path Synopsis
Package hook provides observation points for framework lifecycle events.
Package hook provides observation points for framework lifecycle events.
Package routing provides the core interfaces and components for HTTP routing.
Package routing provides the core interfaces and components for HTTP routing.
Package scope provides lifecycle management strategies for the IoC container.
Package scope provides lifecycle management strategies for the IoC container.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL