Documentation › JSON API › Parsing JSON requests
HTTP requests containing a JSON body can be decoded using the request.DecodeJSON() function from the internal/request package.
For example, if you expect a request body like {"Name": "Dave", "Age": 58}, you can decode it into an input struct like this:
func (app *application) exampleHandler(w http.ResponseWriter, r *http.Request) {
var input struct {
Name string `json:"Name"`
Age int `json:"Age"`
}
err := request.DecodeJSON(w, r, &input)
if err != nil {
app.badRequest(w, r, err)
return
}
fmt.Println(input)
}
Important: The final argument to request.DecodeJSON() is the destination for the decoded JSON, and it must be a non-nil pointer. Passing anything else will cause a panic.
Additionally, if you are decoding into a struct, like in the example above, the destination struct fields must be exported (i.e. start with a capital letter).
If request.DecodeJSON() fails (for example, due to invalid or malformed JSON), it returns a user-friendly error that can be sent directly to the client using the app.badRequest() helper, as demonstrated in the example above.
The internal/request package also provides the request.DecodeJSONStrict() function. This behaves the same as request.DecodeJSON(), except it returns an error if the request includes unknown JSON fields that are not present in the target struct.
The internal/validator package includes a simple (but powerful) validator.Validator type that you can use to carry out validation checks, and you can return any validation errors to the client with the app.failedValidation() helper.
Extending the example above:
func (app *application) exampleHandler(w http.ResponseWriter, r *http.Request) {
var input struct {
Name string `json:"Name"`
Age int `json:"Age"`
}
err := request.DecodeJSON(w, r, &input)
if err != nil {
app.badRequest(w, r, err)
return
}
var vld validator.Validator
vld.CheckField(input.Name != "", "Name", "Name is required")
vld.CheckField(input.Age != 0, "Age", "Age is required")
vld.CheckField(input.Age >= 21, "Age", "Age must be 21 or over")
if input.HasErrors() {
app.failedValidation(w, r, vld)
return
}
fmt.Println(input)
}
The app.failedValidation() helper will send a 422 status code along with any validation error messages. For the example above, the JSON response might look like this:
{
"FieldErrors": {
"Age": "Age must be 21 or over",
"Name": "Name is required"
}
}
Alternatively, you may wish to embed the validator.Validator inside the decode destination, using the `json:"-"` struct tag so that nothing is accidentally decoded into it. For example:
func (app *application) exampleHandler(w http.ResponseWriter, r *http.Request) {
var input struct {
Name string `json:"Name"`
Age int `json:"Age"`
validator.Validator `json:"-"`
}
err := request.DecodeJSON(w, r, &input)
if err != nil {
app.badRequest(w, r, err)
return
}
input.CheckField(input.Name != "", "Name", "Name is required")
input.CheckField(input.Age != 0, "Age", "Age is required")
input.CheckField(input.Age >= 21, "Age", "Age must be 21 or over")
if input.HasErrors() {
app.failedValidation(w, r, input.Validator)
return
}
fmt.Println(input)
}