Documentation
¶
Overview ¶
Package httpx is the routing layer.
It is a thin shell over net/http: Middleware is the standard func(http.Handler) http.Handler, so every middleware written for the Go ecosystem works here unchanged.
Index ¶
- func Chain(h http.Handler, mws ...Middleware) http.Handler
- type Context
- func (c *Context) Ctx() context.Context
- func (c *Context) Fragment(status int, name string, data any) error
- func (c *Context) Input(name string) string
- func (c *Context) JSON(status int, v any) error
- func (c *Context) Param(name string) string
- func (c *Context) Query(name string) string
- func (c *Context) Redirect(to string) error
- func (c *Context) Status(code int) error
- func (c *Context) URL(name string, params ...string) string
- func (c *Context) View(name string, data any) error
- type Creator
- type Destroyer
- type Editor
- type Indexer
- type Middleware
- type Renderer
- type Route
- type Router
- func (r *Router) Action(method, pattern string, h func(*Context) error, mws ...Middleware) *Route
- func (r *Router) Delete(pattern string, h http.HandlerFunc, mws ...Middleware) *Route
- func (r *Router) ForModule(name string) *Router
- func (r *Router) Get(pattern string, h http.HandlerFunc, mws ...Middleware) *Route
- func (r *Router) Group(prefix string, mws ...Middleware) *Router
- func (r *Router) Patch(pattern string, h http.HandlerFunc, mws ...Middleware) *Route
- func (r *Router) Post(pattern string, h http.HandlerFunc, mws ...Middleware) *Route
- func (r *Router) Put(pattern string, h http.HandlerFunc, mws ...Middleware) *Route
- func (r *Router) Resource(name string, controller any) []*Route
- func (r *Router) Routes() []*Route
- func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)
- func (r *Router) Table() *Routes
- func (r *Router) WithRenderer(rd Renderer) *Router
- type Routes
- type Shower
- type Storer
- type Updater
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Context ¶ added in v0.11.0
type Context struct {
// Response and Request are exported: a handler that needs the standard
// library reaches for it directly instead of waiting for a wrapper.
Response http.ResponseWriter
Request *http.Request
// contains filtered or unexported fields
}
Context is what a controller action receives.
It is everything a controller action gets, and no more: the request, the response, and helpers that answer. Nothing more -- and the "nothing more" is the point. There is no database handle here, no repository, no Grant. A controller that could reach the data layer would be a controller that skipped the service, and therefore the policy, and `aru doctor` exists to catch exactly that.
It is a struct rather than an interface because it has no second implementation and never will. One way to do one thing.
func (*Context) Ctx ¶ added in v0.11.0
Ctx returns the request context, which carries the Collector, the logger and the request id.
func (*Context) Fragment ¶ added in v0.11.0
Fragment renders a partial with a status, for HTMX.
The status matters: a form that failed validation answers 422 with the form fragment, and HTMX swaps it in. Answering 200 would make the browser and the logs both believe it worked.
func (*Context) Input ¶ added in v0.11.0
Input reads a form field, from the body or the query string.
Named Input rather than Form because Input is the word the vocabulary already uses for it, and the vocabulary is the point (RULE 10).
func (*Context) JSON ¶ added in v0.11.0
JSON answers with JSON. It exists for the endpoints that are genuinely an API; a page answers with View.
func (*Context) Param ¶ added in v0.11.0
Param reads a path parameter: /invoices/{id} gives Param("id").
func (*Context) Redirect ¶ added in v0.11.0
Redirect answers a redirect, and does the right thing under HTMX.
An HTMX request that gets a 302 follows it inside the fragment, so the whole page ends up nested in a div. HX-Redirect is the header that makes the browser navigate instead. Handling it here means no application has to remember.
func (*Context) URL ¶ added in v0.15.0
URL is the path of a named route, with its parameters filled in order.
ctx.URL("posts.show", post.ID) -> "/posts/01J.../"
It is what a controller hands a view instead of building a path by hand. "/posts/"+id compiles and keeps compiling after the route moves; this stops working the moment the name is wrong, and says so.
An unknown name or a wrong number of parameters returns empty and is logged at ERROR with the name. Empty is what the views already treat as "there is no link here" -- a page with a missing button is recoverable, and a template renderer that panics takes the whole page down to report something a missing link would have said better.
func (*Context) View ¶ added in v0.11.0
View renders a page. The data is a typed struct, never a map.
return ctx.View("invoices/index", IndexData{Invoices: list})
A map would compile and render blank on a typo, which is the failure this framework exists to make impossible. `aru doctor` refuses a map here.
type Middleware ¶
Middleware is the standard net/http signature. We do not invent our own type: that is what keeps the whole Go ecosystem compatible with the framework.
type Renderer ¶ added in v0.11.0
type Renderer interface {
Render(ctx context.Context, w http.ResponseWriter, status int, name string, data any) error
}
Renderer draws a named view with typed data.
It is an interface here, and implemented in the view package, for one reason: the view package imports httpx to register its route, so httpx importing the view package back would be a cycle. The kernel wires the concrete one at boot.
type Route ¶
type Route struct {
Method string
Pattern string
Module string
// contains filtered or unexported fields
}
Route is metadata, used by `aru routes` and by the error page.
func (*Route) Name ¶
Name gives the route a name, so a URL can be generated from it instead of written by hand.
Route.Get("/", home).Name("home")
Route.Resource("invoices", InvoiceController{}) // names them all
It returns the route so the call chains, and the declaration reads as one line: Route.Get("/", home).Name("home").
The name was a field on Route from the first version and was never filled in. A field that nothing writes is a promise the code does not keep -- `aru routes` printed an empty column, and there was no way to generate a URL.
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router is a thin shell over http.ServeMux, which since Go 1.22 already handles methods and path parameters. It exists for groups, per-group middleware and route metadata -- the metadata is what lets the CLI generate typed URL helpers and what the error page uses to show the matched route.
func (*Router) Action ¶ added in v0.11.0
Action registers one controller action, for a route outside a resource.
Route.Action("GET", "/dashboard", dashboard.Index).Name("dashboard")
func (*Router) Delete ¶
func (r *Router) Delete(pattern string, h http.HandlerFunc, mws ...Middleware) *Route
Delete registers a DELETE route.
func (*Router) ForModule ¶
ForModule returns a sub-router that tags its routes with the module name, so `aru routes` can group them. The Kernel calls it for each module.
func (*Router) Get ¶
func (r *Router) Get(pattern string, h http.HandlerFunc, mws ...Middleware) *Route
Get registers a GET route.
func (*Router) Group ¶
func (r *Router) Group(prefix string, mws ...Middleware) *Router
Group returns a sub-router with the prefix appended and the middleware inherited. The route table is shared with the parent.
func (*Router) Patch ¶
func (r *Router) Patch(pattern string, h http.HandlerFunc, mws ...Middleware) *Route
Patch registers a PATCH route.
func (*Router) Post ¶
func (r *Router) Post(pattern string, h http.HandlerFunc, mws ...Middleware) *Route
Post registers a POST route.
func (*Router) Put ¶
func (r *Router) Put(pattern string, h http.HandlerFunc, mws ...Middleware) *Route
Put registers a PUT route.
func (*Router) Resource ¶ added in v0.11.0
Resource registers the REST routes a controller implements.
Route.Resource("invoices", InvoiceController{})
The seven, in the conventional order and with the conventional names:
GET /invoices index invoices.index
GET /invoices/create create invoices.create
POST /invoices store invoices.store
GET /invoices/{id} show invoices.show
GET /invoices/{id}/edit edit invoices.edit
PUT /invoices/{id} update invoices.update
PATCH /invoices/{id} update invoices.update
DELETE /invoices/{id} destroy invoices.destroy
The order matters: /invoices/create is registered before /invoices/{id} so a GET of "create" reaches the form rather than being read as an id. Go's ServeMux prefers the more specific pattern, and registering in this order keeps the intent readable even where the mux would sort it out anyway.
A controller implementing none of the seven registers nothing and returns zero routes, which is a wiring mistake worth seeing in `aru routes`.
func (*Router) ServeHTTP ¶
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)
ServeHTTP dispatches to the underlying mux.
func (*Router) Table ¶ added in v0.11.0
Table returns the route table, for URL generation and for `aru routes`.
func (*Router) WithRenderer ¶ added in v0.11.0
WithRenderer returns a router whose handlers can render views.
The kernel calls it at boot with the view module. Without it, Context.View returns an error naming the missing line in bootstrap/app.go rather than panicking.
type Routes ¶ added in v0.11.0
type Routes struct {
// contains filtered or unexported fields
}
Routes is the table of registered routes, and the index by name.
func (*Routes) All ¶ added in v0.11.0
All returns the routes in registration order, for `aru routes`.
func (*Routes) Must ¶ added in v0.11.0
Must is URL for the places that cannot handle an error -- a template helper, mostly. It returns the message as the href, so a broken link says what is wrong instead of pointing at "/".
func (*Routes) URL ¶ added in v0.11.0
URL builds the path of a named route, filling the parameters in order.
URL("home") -> "/"
URL("invoices.show", "42") -> "/invoices/42"
A hardcoded "/invoices/"+id compiles and keeps compiling after the route moves. This does not: an unknown name or a wrong number of parameters is an error the caller sees, not a 404 the user sees.
It returns an error rather than panicking, because a URL is often built from data -- and a panic in a template renderer takes the whole page down to report something a broken link would have said better.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package middleware holds the mandatory request pipeline.
|
Package middleware holds the mandatory request pipeline. |