算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
web-applications【设计与创意】Builds production Go web applications with HTTP handlers, routing, middleware, template rendering, and REST API design following idiomatic Go patterns.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
name: web-applications description: Builds production Go web applications with HTTP handlers, routing, middleware, template rendering, and REST API design following idiomatic Go patterns. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: go role: implementation scope: implementation output-format: code content-types:
Senior web engineer building production Go web applications with idiomatic HTTP handling, clean routing, reusable middleware, template rendering, and RESTful API design. This skill covers everything from basic handlers to WebSocket connections.
(http.ResponseWriter, *http.Request) — never store request globallyfunc(http.Handler) http.Handler patternhtml/template for XSS protectionUse this skill when:
html/templateAvoid this skill for:
Define the Router — Choose an explicit router and register all routes with typed handlers.
Checkpoint: No routes use http.DefaultServeMux — all routes are explicitly registered.
Build Middleware — Compose handlers using the func(http.Handler) http.Handler pattern for reusability.
Checkpoint: Each middleware is independent and can be reordered without side effects.
Implement Handlers — Each handler reads from the request, calls business logic, and writes a response. Checkpoint: Handlers never contain business logic — they delegate to service layers.
Standardize Responses — All API responses follow a consistent JSON structure with status codes. Checkpoint: Every handler returns the correct HTTP status code (200, 201, 400, 404, 500, etc.)
Add Error Handling — Centralized error recovery middleware catches panics and returns structured errors. Checkpoint: No panic reaches the production server — all recoverable errors are handled.
Explicit routing and clean handler delegation are the foundation of maintainable web apps.
// ❌ BAD: all routes on DefaultServeMux, handlers contain business logic, no error handling
func main() {
http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
// Handler does everything: parsing, business logic, DB access, response
switch r.Method {
case http.MethodGet:
users, _ := db.Query("SELECT * FROM users") // error ignored
json.NewEncoder(w).Encode(users)
case http.MethodPost:
var user User
json.NewDecoder(r.Body).Decode(&user) // error ignored
db.Exec("INSERT INTO users VALUES ($1, $2)", user.ID, user.Name) // error ignored
w.WriteHeader(http.StatusCreated)
}
})
// No middleware, no timeout, no graceful shutdown
http.ListenAndServe(":8080", nil)
}
What's wrong:
DefaultServeMux cannot match methods or paths with parameters// setupRouter creates and configures the application router with middleware and handlers.
func setupRouter(cfg *Config, svc *UserService) http.Handler {
mux := http.NewServeMux()
// Apply middleware stack (outermost to innermost)
var handler http.Handler = mux
handler = loggingMiddleware(handler)
handler = recoveryMiddleware(handler)
handler = authMiddleware(cfg)
handler = requestIDMiddleware(handler)
// Register routes with method-based dispatch
mux.HandleFunc("GET /api/health", healthHandler)
mux.HandleFunc("GET /api/users", listUsersHandler(svc))
mux.HandleFunc("POST /api/users", createUserHandler(svc))
mux.HandleFunc("GET /api/users/{id}", getUserHandler(svc))
mux.HandleFunc("PUT /api/users/{id}", updateUserHandler(svc))
mux.HandleFunc("DELETE /api/users/{id}", deleteUserHandler(svc))
return handler
}
// listUsersHandler returns an HTTP handler that lists all users.
func listUsersHandler(svc *UserService) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Read and validate request parameters
page := queryInt(r, "page", 1)
limit := queryInt(r, "limit", 25)
if limit < 1 || limit > 100 {
limit = 25
}
// Delegate to service layer
users, total, err := svc.ListUsers(r.Context(), page, limit)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
// Write structured response
writeJSON(w, http.StatusOK, map[string]any{
"users": users,
"total": total,
"page": page,
"limit": limit,
})
}
}
// writeJSON encodes data as JSON and writes it to the response.
func writeJSON(w http.ResponseWriter, status int, data any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
if err := json.NewEncoder(w).Encode(data); err != nil {
// Response body write failure — connection likely closed
log.Printf("failed to write response: %v", err)
}
}
// writeError writes a structured JSON error response.
func writeError(w http.ResponseWriter, status int, err error) {
writeJSON(w, status, map[string]any{
"error": map[string]string{
"message": err.Error(),
"code": http.StatusText(status),
},
})
}
Why this works:
ServeMux with Go 1.22+ method-based routing ("GET /api/users")handler = middleware(handler)writeJSON and writeError are reusable helpersMiddleware should be composable, independent, and orderable.
// ❌ BAD: middleware has side effects and depends on global state
var requestCount int
func middleware1(w http.ResponseWriter, r *http.Request) {
requestCount++ // global mutable state — not thread-safe
if r.URL.Path == "/admin" {
// Hardcoded auth check — cannot be reused
if r.Header.Get("Authorization") != "secret-token" {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
}
// Calls next handler directly — cannot be composed
nextHandler(w, r) // implicit next handler — where does it come from?
}
What's wrong:
requestCount) is not thread-safenextHandler global// loggingMiddleware logs each request's method, path, status, and duration.
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// Wrap the response writer to capture the status code
wrapper := &responseWriterWrapper{ResponseWriter: w, statusCode: http.StatusOK}
next.ServeHTTP(wrapper, r)
duration := time.Since(start)
log.Printf("%s %s %d %s", r.Method, r.URL.Path, wrapper.statusCode, duration)
})
}
// recoveryMiddleware recovers from panics and returns a 500 error.
func recoveryMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if rec := recover(); rec != nil {
log.Printf("panic recovered: %v\n%s", rec, debug.Stack())
writeError(w, http.StatusInternalServerError, fmt.Errorf("internal server error"))
}
}()
next.ServeHTTP(w, r)
})
}
// authMiddleware validates the Authorization header and passes the user to the handler.
func authMiddleware(cfg *Config) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
writeError(w, http.StatusUnauthorized, errors.New("missing authorization header"))
return
}
// Extract user from token (implementation detail delegated to auth service)
token := strings.TrimPrefix(authHeader, "Bearer ")
user, err := validateToken(cfg.JWTSecret, token)
if err != nil {
writeError(w, http.StatusUnauthorized, fmt.Errorf("invalid token: %w", err))
return
}
// Attach user to context for downstream handlers
ctx := context.WithValue(r.Context(), userContextKey, user)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
// responseWriterWrapper captures the status code written by the handler.
type responseWriterWrapper struct {
http.ResponseWriter
statusCode int
}
func (w *responseWriterWrapper) WriteHeader(code int) {
w.statusCode = code
w.ResponseWriter.WriteHeader(code)
}
Why this works:
func(http.Handler) http.Handler — universal compositionresponseWriterWrapper captures status codes without modifying ResponseWriter interfaceServer-side HTML rendering must protect against XSS using html/template.
// ❌ BAD: renders user input directly as HTML — XSS vulnerability
func renderProfile(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name") // unsanitized user input
html := fmt.Sprintf(`<html><body><h1>Welcome, %s</h1></body></html>`, name)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprint(w, html) // user input injected directly into HTML
}
What's wrong:
name is user-controlled input injected directly into HTML?name=<script>alert('xss')</script> to execute arbitrary JSfmt.Sprintf does no escaping — html/template is the safe alternative// profileTemplateCache caches parsed templates for performance.
var profileTemplate = template.Must(template.New("profile").Parse(`
<!DOCTYPE html>
<html>
<head><title>Welcome</title></head>
<body>
<h1>Welcome, {{.Name}}</h1>
<p>Email: {{.Email}}</p>
<p>Joined: {{.Joined.Format "January 2, 2006"}}</p>
</body>
</html>
`))
// renderProfile renders the user profile page with safe HTML escaping.
func renderProfile(w http.ResponseWriter, r *http.Request) {
// Extract user data from context (populated by auth middleware)
user := r.Context().Value(userContextKey).(*User)
// Prepare template data
data := map[string]any{
"Name": user.Name,
"Email": user.Email,
"Joined": user.CreatedAt,
}
// html/template automatically escapes all output — XSS is impossible
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := profileTemplate.Execute(w, data); err != nil {
log.Printf("template execute error: %v", err)
writeError(w, http.StatusInternalServerError, errors.New("template error"))
}
}
Why this works:
html/template automatically escapes all output — XSS is impossible even if templates are user-modifiedtemplate.Must) — no per-request parse costhttp.NewServeMux() with Go 1.22+ method-based routing ("GET /path") — never http.DefaultServeMuxfunc(http.Handler) http.Handler — universal composition patternhtml/template (not template) for all HTML output — XSS protection is automaticcontext.Context in all handlers and propagate it to downstream callscontext.WithTimeout for all external I/O calls within handlersContent-Type header on all responses before writing the bodyfmt.Sprintf or string concatenation to build HTML — always use html/template*http.Request or http.ResponseWriter in global statejson.Encoder.Encode or template.Executehttp.ListenAndServe without a timeout configurationhttp.HandleFunc with "/" — this registers on DefaultServeMuxWriteHeader or json.Encode)| Skill | Purpose |
|---|---|
cloud-development | Graceful shutdown, health checks, and configuration for production |
best-practices | Error handling, naming, and Go idioms across the codebase |
database-patterns | Repository patterns and transaction management for data access |
modular-design | Package boundaries and service layer architecture |