http.Handler Interface
Part of the Object Oriented Programming section of Coddy's GO journey — lesson 77 of 107.
The http.Handler interface is the foundation of Go's web server architecture. It defines how HTTP requests are processed, and any type implementing it can serve web requests.
The interface is remarkably simple:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}Any type with a ServeHTTP method can handle HTTP requests. Here's a custom handler:
type GreetHandler struct {
Greeting string
}
func (h GreetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s, visitor!", h.Greeting)
}You can use this handler directly with a server:
handler := GreetHandler{Greeting: "Welcome"}
http.Handle("/greet", handler)For simpler cases, Go provides http.HandlerFunc, a type that lets you convert a regular function into a handler:
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello!")
}
http.Handle("/hello", http.HandlerFunc(hello))The power of this interface becomes clear when you realize that handlers can wrap other handlers. This enables middleware patterns where you add logging, authentication, or other cross-cutting concerns without modifying the original handler logic.
Challenge
EasyLet's build a simple HTTP handler system that simulates how web requests are processed! Since we can't run an actual HTTP server in this environment, you'll create handlers that implement the http.Handler interface and demonstrate how they process requests by working with mock request/response objects.
You'll organize your code across two files:
handlers.go: Define your custom HTTP handlers.Create a
MockResponseWriterstruct that captures what would be written to an HTTP response. It should store the response body as a string and implement aWrite(data []byte) (int, error)method that appends data to the body. Add aBody() stringmethod to retrieve the accumulated response.Create a
MockRequeststruct withPathandMethodstring fields to simulate an HTTP request.Now create two handler structs that implement the
http.Handlerinterface pattern (with aServeHTTPmethod that takes a*MockResponseWriterand*MockRequest):WelcomeHandlerwith aMessagefield - writes the message followed by the request pathMethodHandler- writes different responses based on the request method:"Fetching data"for GET,"Creating resource"for POST, and"Method not supported"for anything else
main.go: Process requests through your handlers.Read a handler type (
welcomeormethod), then read request details. For the welcome handler, also read the welcome message.Create the appropriate handler, build a mock request, and call
ServeHTTPto process it. Print the response body.
The following inputs will be provided:
- Line 1: Handler type (
welcomeormethod) - Line 2: Request path
- Line 3: Request method
- Line 4 (only for welcome): Welcome message
For example, given:
welcome
/home
GET
Hello fromYour output should be:
Hello from /homeAnd given:
method
/api/users
POSTYour output should be:
Creating resourceAnd given:
method
/api/data
GETYour output should be:
Fetching dataAnd given:
method
/api/items
DELETEYour output should be:
Method not supportedThis challenge demonstrates the core concept of the http.Handler interface—any type with a ServeHTTP method can handle requests. In real applications, the http.ResponseWriter and *http.Request come from Go's standard library, but the pattern of implementing handlers remains exactly the same.
Cheat sheet
The http.Handler interface is the foundation of Go's web server architecture:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}Any type with a ServeHTTP method can handle HTTP requests:
type GreetHandler struct {
Greeting string
}
func (h GreetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s, visitor!", h.Greeting)
}Register a handler with the server:
handler := GreetHandler{Greeting: "Welcome"}
http.Handle("/greet", handler)For simpler cases, use http.HandlerFunc to convert a function into a handler:
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello!")
}
http.Handle("/hello", http.HandlerFunc(hello))Handlers can wrap other handlers, enabling middleware patterns for logging, authentication, or other cross-cutting concerns.
Try it yourself
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Read handler type
scanner.Scan()
handlerType := scanner.Text()
// Read request path
scanner.Scan()
path := scanner.Text()
// Read request method
scanner.Scan()
method := scanner.Text()
// Create mock request
request := &MockRequest{
Path: path,
Method: method,
}
// Create mock response writer
response := &MockResponseWriter{}
// TODO: Based on handlerType, create the appropriate handler
// If handlerType is "welcome", read the welcome message and create WelcomeHandler
// If handlerType is "method", create MethodHandler
// Call ServeHTTP on the handler with response and request
if handlerType == "welcome" {
scanner.Scan()
message := scanner.Text()
// TODO: Create WelcomeHandler with the message and call ServeHTTP
_ = message // Remove this line when implementing
} else if handlerType == "method" {
// TODO: Create MethodHandler and call ServeHTTP
}
// Print the response body
fmt.Println(response.Body())
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of Go OOP
External FilesGo Workspace & ModulesPackages & ImportsExported vs Unexported NamesIntroduction to OOP in GoStructs as ClassesDefining Methods on StructsPointer vs Value ReceiversStruct InitializationConstructor FunctionsRecap - Simple Calculator4Interfaces
Introduction to InterfacesImplicit ImplementationInterface as ContractEmpty Interface (any)Type AssertionType SwitchInterface CompositionStringer & Error InterfacesRecap - Shape Calculator7Encapsulation
Exported vs Unexported FieldsPackage-Level EncapsulationGetter & Setter MethodsInformation Hiding in GoRecap - Student Records10Generics (Go 1.18+)
Introduction to GenericsType ParametersType ConstraintsGeneric StructsGeneric Methods WorkaroundRecap - Generic Collection2Types & Structs Deep Dive
Basic & Composite TypesCustom Type DefinitionsStruct TagsAnonymous StructsNested StructsZero Values & DefaultsRecap - Contact Book5Composition Over Inheritance
Why Go Has No InheritanceStruct Embedding BasicsMethod PromotionEmbedding Multiple StructsEmbedding vs AggregationShadowing Embedded MethodsRecap - Employee Hierarchy8Error Handling & OOP
The error InterfaceCustom Error TypesError Wrapping (fmt.Errorf)Sentinel Errorserrors.Is() and errors.As()Panic, Defer, and RecoverRecap - File Parser11Standard Library & OOP
io.Reader & io.Writersort.Interfacefmt.Stringer Interfaceencoding/json with Structshttp.Handler InterfaceRecap - REST API Models3Pointers & Memory
Pointer Basics in GoPointers to StructsPass by Value vs ReferenceThe new() FunctionGarbage Collection in GoRecap - Linked List Builder6Polymorphism in Go
Polymorphism via InterfacesDuck Typing in GoInterface Satisfaction RulesPolymorphic CollectionsDependency InjectionRecap - Payment Processor9Concurrency & OOP
Goroutines BasicsChannels & CommunicationBuffered vs Unbuffered ChanSelect Statementsync.Mutex & sync.RWMutexsync.WaitGroupThread-Safe Struct DesignRecap - Worker Pool