Web Development with Go
45 minGo's `net/http` package provides comprehensive HTTP server and client functionality, making it easy to build web services and APIs. The package includes HTTP server, client, request/response handling, and routing capabilities. Go's standard library is powerful enough for many web applications without external frameworks. Understanding `net/http` is essential for Go web development.
Handlers process HTTP requests and responses, implementing the `http.Handler` interface with a `ServeHTTP(w http.ResponseWriter, r *http.Request)` method. Handler functions can be registered with `http.HandleFunc()` or `http.Handle()`. Handlers can access request data (headers, body, query parameters) and write responses. Understanding handlers enables you to build web endpoints.
Go supports JSON encoding/decoding through the `encoding/json` package, making it easy to build REST APIs. The `json` package can encode Go structs to JSON and decode JSON to structs using struct tags. JSON is the standard format for modern APIs. Understanding JSON handling enables you to build API endpoints that exchange structured data.
Middleware can be used to add functionality to handlers, such as logging, authentication, CORS, or request processing. Middleware functions wrap handlers, allowing you to execute code before and after handler execution. Middleware enables cross-cutting concerns to be handled consistently. Understanding middleware helps you build maintainable web applications.
Routing can be handled with the standard library or frameworks like Gorilla Mux, Gin, or Echo. The standard library provides basic routing; frameworks add features like parameter extraction, middleware chains, and route groups. Understanding routing helps you organize your web application's endpoints.
Best practices include using proper HTTP methods and status codes, handling errors appropriately, using middleware for cross-cutting concerns, validating input, and structuring handlers clearly. Web applications should be secure, performant, and maintainable. Understanding web development in Go enables you to build production-ready web services.
Key Concepts
- net/http provides HTTP server and client functionality.
- Handlers process HTTP requests and responses.
- JSON encoding/decoding for API data exchange.
- Middleware adds functionality to handlers.
- Go's standard library is powerful for web development.
Learning Objectives
Master
- Creating HTTP servers and handlers
- Handling JSON encoding and decoding
- Implementing middleware for cross-cutting concerns
- Building RESTful APIs with Go
Develop
- Web application architecture thinking
- Understanding HTTP and REST principles
- Designing maintainable web services
Tips
- Use proper HTTP methods (GET, POST, PUT, DELETE) and status codes.
- Handle errors appropriately and return meaningful error messages.
- Use middleware for logging, authentication, and CORS.
- Validate input data before processing.
Common Pitfalls
- Not handling errors properly, causing panics.
- Not setting proper Content-Type headers.
- Not validating input, causing security vulnerabilities.
- Creating handlers that are too large or do too much.
Summary
- Go's net/http package provides powerful web development capabilities.
- Handlers process HTTP requests and generate responses.
- JSON encoding/decoding enables API data exchange.
- Middleware adds cross-cutting functionality to handlers.
- Understanding web development enables building production-ready services.
Exercise
Create a simple web server with handlers and JSON responses.
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"time"
)
// User struct for JSON
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Created string `json:"created"`
}
// Response struct
type Response struct {
Success bool `json:"success"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// Global users slice
var users = []User{
{ID: 1, Name: "Alice", Email: "alice@example.com", Created: time.Now().Format(time.RFC3339)},
{ID: 2, Name: "Bob", Email: "bob@example.com", Created: time.Now().Format(time.RFC3339)},
}
// Middleware for logging
func loggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
}
}
// Handler for home page
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Welcome to Go Web Server</h1>")
fmt.Fprintf(w, "<p>Available endpoints:</p>")
fmt.Fprintf(w, "<ul>")
fmt.Fprintf(w, "<li><a href='/api/users'>GET /api/users</a></li>")
fmt.Fprintf(w, "<li>POST /api/users (with JSON body)</li>")
fmt.Fprintf(w, "<li><a href='/api/users/1'>GET /api/users/{id}</a></li>")
fmt.Fprintf(w, "</ul>")
}
// Handler for getting all users
func getUsersHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
response := Response{
Success: true,
Message: "Users retrieved successfully",
Data: users,
}
json.NewEncoder(w).Encode(response)
}
// Handler for getting a specific user
func getUserHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
// Extract ID from URL
idStr := r.URL.Path[len("/api/users/"):]
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Invalid user ID", http.StatusBadRequest)
return
}
// Find user
for _, user := range users {
if user.ID == id {
response := Response{
Success: true,
Message: "User found",
Data: user,
}
json.NewEncoder(w).Encode(response)
return
}
}
// User not found
response := Response{
Success: false,
Message: "User not found",
}
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(response)
}
// Handler for creating a new user
func createUserHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
w.Header().Set("Content-Type", "application/json")
var newUser User
if err := json.NewDecoder(r.Body).Decode(&newUser); err != nil {
response := Response{
Success: false,
Message: "Invalid JSON",
}
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(response)
return
}
// Generate new ID
newUser.ID = len(users) + 1
newUser.Created = time.Now().Format(time.RFC3339)
users = append(users, newUser)
response := Response{
Success: true,
Message: "User created successfully",
Data: newUser,
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(response)
}
func main() {
// Define routes
http.HandleFunc("/", loggingMiddleware(homeHandler))
http.HandleFunc("/api/users", loggingMiddleware(getUsersHandler))
http.HandleFunc("/api/users/", loggingMiddleware(getUserHandler))
http.HandleFunc("/api/users/create", loggingMiddleware(createUserHandler))
// Start server
fmt.Println("Server starting on http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}