The Input Boundary: Binding, Validation, and Error Ownership
For Gin, request binding is the point where an HTTP stream becomes application data, and the important question is who controls that interpretation and its failure. This chapter examines how method and content type select a binder, how structural validation fits into the result, and why Bind and ShouldBind are different response contracts rather than stylistic alternatives. It also shows when replaying a body or adding a custom parser is worth the memory and maintenance cost.
Binding starts with the request source
An endpoint usually wants a useful Go value—a login request, a search filter, or a path parameter—not a raw HTTP message. But the same conceptual value can arrive through different sources. A query string is part of the URL, a form is parsed from request data, a JSON document is a body with a media type, and a URI parameter was captured by the route tree. A binder is the component that knows how to move from one of those sources into a destination value.
Gin exposes binding interfaces with a common shape: a binder has a name and a method that receives an http.Request and a destination. The built-in set covers JSON, XML, YAML, TOML, MessagePack, Protocol Buffers, BSON, forms, queries, URI parameters, and headers. The list is less important than the seam it reveals. Gin's context chooses or receives an interpreter; the interpreter maps data; validation can then inspect the resulting value.
Automatic binding uses the request method and content type. The general Bind or ShouldBind form is useful when an endpoint is intentionally able to accept the formats represented by that selection. For a route whose contract is specifically JSON, an explicit method such as ShouldBindJSON makes the decision visible. Explicit binding is also helpful when a client sends a content type that could otherwise select an unintended or overly broad path through automatic selection.
A GET request illustrates why the method is part of the choice. Gin's default selection uses form-style binding for a GET, which means a query-oriented endpoint can bind URL values without pretending that it has a request body to decode. For a non-GET request, the content type helps select a body or form binder. The endpoint's contract should therefore be read together with the request method: “bind this value” is incomplete unless it is clear where the value is expected to come from.
The destination also matters. Gin's binding layer maps into a pointer to a value and can apply tags that identify external field names. A JSON field may use a JSON tag, while a query or form field may use a form tag. These tags are not decoration; they are the mapping between an external protocol and an internal type. A missing or incorrect tag can leave a field empty without making the route itself invalid.
The main choice is who owns a binding failure
Gin provides two families of binding behavior because input failure is also a response decision.
The must-bind family—available through Bind, BindJSON, BindXML, BindQuery, and related methods—uses an aborting path when binding fails. Gin writes a client-error status and attaches the binding error to the context. This is concise for an endpoint whose malformed input should always produce the framework's immediate failure response.
The should-bind family—ShouldBind, ShouldBindJSON, and their explicit counterparts—returns the error to the handler without setting the response status or aborting the chain.