CodeRigor

LESSON

Commit one consistent JSON response

Encode before committing headers, preserve JSON and length metadata, suppress HEAD bodies and propagate write failures.

A response has a status, headers and body. They form one contract. A JSON body with an HTML media type, a false success status or the wrong byte length is not fixed by a visually correct payload.

The response helper centralizes those mechanics. It encodes the value before committing the response, sets the JSON media type and nosniff header, and records the exact encoded length including one terminal newline.

Prepare before committing
start / 3
Encoding can fail before any response is committed. Headers and status then describe the exact bytes that will be written.
Prepare before committing
Encoding can fail before any response is committed. Headers and status then describe the exact bytes that will be written.

Use encoding/json rather than concatenating user input into JSON text. The supplied encoding helper returns bytes or an error. A value such as a non-finite float cannot be represented by ordinary JSON, so the failure must occur before success headers are sent.

Call Header().Set before WriteHeader. A later attempt to change the status does not replace the first committed status.

Go
package main
import ("fmt";"net/http/httptest")
func main(){
w:=httptest.NewRecorder();w.Header().Set("Content-Type","application/json; charset=utf-8");w.WriteHeader(201);_,_=w.Write([]byte("{}\n"));fmt.Println(w.Code,w.Header().Get("Content-Type"),w.Body.Len())
}

HEAD carries the representation headers without writing its body. For the health response, the encoded object plus newline is 16 bytes; both GET and HEAD advertise that length, while HEAD writes zero body bytes.

After commitment, a network write can fail. Returning that error is useful, but writing a second JSON error response cannot safely undo a partially sent result. The final handler writes only encodable fixed structs/maps and ignores the returned error because there is no valid second response to send; a later instrumentation layer can observe failures without exposing private details.

The helper is deliberately small. It does not authenticate requests, set cross-origin policy or establish business correctness. Those belong to explicit application boundaries.

HEAD describes a representation without its bytes
start / 3
The JSON media type and representation length stay aligned with GET. Only the response body is suppressed.
HEAD describes a representation without its bytes
The JSON media type and representation length stay aligned with GET. Only the response body is suppressed.
Go
package main
import ("fmt";"encoding/json")
func main(){
b,_:=json.Marshal(map[string]string{"message":"<tag>"});var value map[string]string;_ =json.Unmarshal(b,&value);fmt.Println(value["message"])
}

Complete the helper and test encoding failure, a writer failure, embedded data characters and HEAD. Keep byte length distinct from rune count.

Next we put state behind a repository interface so handlers can be tested without a database.

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →

Optional design discussion of handlers, dependencies and tests. This 2019 talk predates current Go routing changes; use the current net/http documentation and this course’s explicit route contract for the lab.