Documentation › JSON API › Parsing JSON requests
HTTP requests containing a JSON body can be decoded using the functions int the internal/request package.
For example, if you expect a request body like containing a JSON object like {"Name": "Dave", "Age": 58}, you can decode it into an input struct by using the request.DecodeJSONObject() function 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.DecodeJSONObject(w, r, &input)
if err != nil {
var badRequestErr *request.BadRequestError
switch {
case errors.As(err, &badRequestErr):
app.badRequest(w, r, err)
default:
app.serverError(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.DecodeJSONObject() fails (for example, due to invalid or malformed JSON), it returns a user-friendly error with the type *request.BadRequestError 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.DecodeJSONArray() function, that can be used to decode JSON arrays in the same way.
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 {
var badRequestErr *request.BadRequestError
switch {
case errors.As(err, &badRequestErr):
app.badRequest(w, r, err)
default:
app.serverError(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 {
var badRequestErr *request.BadRequestError
switch {
case errors.As(err, &badRequestErr):
app.badRequest(w, r, err)
default:
app.serverError(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)
}