Core Framework
Dispatcher
Understand how GoLazy runs middleware, routes requests, and serves public files.
Dispatcher responsibilities
golazy.dev/lazydispatch owns request dispatch inside a lazyapp
application.
In the normal application flow, you do not create a dispatcher directly.
lazyapp.New creates it, installs the framework middleware, and returns one
application http.Handler.
Dispatch currently owns:
- Middleware registration and execution order.
- Router middleware.
- Public static-file middleware.
405 Method Not Allowedresponses for public files.- Fallthrough to
404 Not Found.
It is the intended home for later request logic:
- Response buffering.
ETagandLast-Modifiedconditional responses.- Request monitoring and tracing hooks.
- Cookie lifecycle.
- Session lifecycle.
- Flash lifecycle.
HEADresponse adaptation.- Action error response conversion.
Routing remains in lazyroutes. Rendering remains in lazycontroller.
Dispatch in lazyapp
Applications configure dispatch through lazyapp.Config:
func App() *lazyapp.App {
return lazyapp.New(lazyapp.Config{
Name: "sample_app",
Drawer: Draw,
Public: app.Public,
Views: app.Views,
Context: Context,
Middlewares: []lazydispatch.Middleware{
requestIDMiddleware,
},
})
}
lazyapp.New builds this chain:
application middleware
router middleware
public static-file middleware
404 final handler
That means:
- Application middleware sees the request first.
- If a registered route owns the path, the router handles the request.
- If no route owns the path, public files get a chance to serve it.
- If neither handles it, dispatch returns
404 Not Found.
Middleware interface
Middleware implements one interface:
type Middleware interface {
Handler(next http.Handler) http.Handler
}
This is deliberately close to standard library handler composition. A middleware receives the next handler and returns the wrapped handler.
For simple middleware, MiddlewareFunc adapts a function:
var requestIDMiddleware = lazydispatch.MiddlewareFunc(
func(next http.Handler) http.Handler {
return http.HandlerFunc(func(
w http.ResponseWriter,
r *http.Request,
) {
next.ServeHTTP(w, r)
})
},
)
Then add it through lazyapp.Config.Middlewares.
Middleware order
Application middlewares run in the order they are listed:
Middlewares: []lazydispatch.Middleware{
first,
second,
},
The request enters first, then second, then the framework router and public
middleware. The response unwinds in the opposite direction.
Middleware should normally call next.ServeHTTP. If it does not, it has
handled the request and the router/public-file fallback will not run.
Router middleware
lazyapp.New installs the route scope as dispatch router middleware.
The router middleware expects:
type RouteHandler interface {
http.Handler
HandlesPath(path string) bool
}
lazyroutes.Scope satisfies this interface.
For each request, dispatch asks whether any registered route owns the path. If yes, the request is sent to the route scope. The standard library mux then performs method matching, path-value extraction, and method-not-allowed behavior.
If no route owns the path, dispatch continues to public files.
This is the important split: the router owns application route matching, while dispatch owns fallback behavior.
Static-file middleware
lazyapp.New installs public-file middleware when lazyapp.Config.Public is set:
Public: app.Public,
Public returns the embedded public filesystem:
func Public() (fs.FS, error) {
return fs.Sub(Files, "public")
}
Static middleware checks whether the requested file exists before serving it.
Missing files fall through to the final not-found handler. Existing files are
served with http.FileServerFS.
For an existing public file:
GET /styles.css
the file server sends the response.
For a missing public file:
GET /missing.txt
dispatch falls through to 404 Not Found.
For unsupported methods on existing files:
POST /styles.css
dispatch returns:
405 Method Not Allowed
Allow: GET
Application routes are checked before public files, so a controller route can use a path that also looks like a file path.
Method not allowed
Static-file dispatch returns 405 Method Not Allowed for unsupported methods
on existing files.
Application route method handling currently comes from the standard library
mux. For example, a POST to a GET route is handled by the route scope's
embedded mux.
Planned request middlewares
The dispatcher is where request-wide behavior will move.
Response buffering will capture status, headers, and body before bytes are committed. This will allow template or view failures to become clean error responses instead of partial HTML.
Conditional responses will use final headers and body state to evaluate
If-None-Match and If-Modified-Since, returning 304 Not Modified when
appropriate.
Request monitoring will record method, path, route name, controller/action, status, response size, duration, and allocation deltas.
Cookie, session, and flash support will use request-local dispatch state so controllers and views can read and update them without writing directly to the response.
HEAD support will preserve GET headers while suppressing the response body
at commit time.
How to use without lazyapp
Most applications should configure dispatch through lazyapp.New. Use
lazydispatch directly only when testing middleware or building a custom
application assembly.
Create a dispatcher manually:
dispatcher := lazydispatch.NewDispatcher()
Register middleware manually:
dispatcher.Use(first)
dispatcher.Use(second)
Install a router manually:
dispatcher.Use(lazydispatch.Router(router))
Install public files manually:
dispatcher.Use(lazydispatch.Public(publicFS))
Use the dispatcher as a handler:
server := &http.Server{
Addr: ":8080",
Handler: dispatcher,
}
Or build an explicit handler chain:
handler := dispatcher.Handler(http.NotFoundHandler())
Manual assembly means you are responsible for creating the route scope, initializing views, initializing application context, and ordering middleware correctly.