CodeBlanks Logo Icon CodeBlanks

Challenge 2 - HTTP service

Write a JSON-returning service with net/http.

Challenge: a notes API

You will write a small HTTP service that keeps notes in memory:

  • POST /notlar: adds a note with a {"metin":"..."} body and returns 201 with the added note (including its id). An empty text gives 400.
  • GET /notlar: returns all notes as a JSON array.
type Not struct {
    ID    int    `json:"id"`
    Metin string `json:"metin"`
}

The notes are kept in a slice protected by a sync.Mutex.

Plan

1) type Depo struct { mu sync.Mutex; notlar []Not; sonID int }. 2) mux.HandleFunc("POST /notlar", ...): decode the body, 400 if empty, otherwise lock, sonID++, append, 201 + JSON. 3) mux.HandleFunc("GET /notlar", ...): lock and write the list as JSON. 4) Send a POST and a GET with httptest and check the result.

Practice Build the service

Implement the plan. Send a POST with the body {"metin":"süt al"}, then print the body of the GET response.

Starter code

main.go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "net/http/httptest"
    "strings"
    "sync"
)

func main() {
    // buraya yaz
}

Sample solution

main.go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "net/http/httptest"
    "strings"
    "sync"
)

type Not struct {
    ID    int    `json:"id"`
    Metin string `json:"metin"`
}

type Depo struct {
    mu     sync.Mutex
    notlar []Not
    sonID  int
}

func main() {
    d := &Depo{notlar: []Not{}}
    mux := http.NewServeMux()
    mux.HandleFunc("POST /notlar", func(w http.ResponseWriter, r *http.Request) {
        var n Not
        if err := json.NewDecoder(r.Body).Decode(&n); err != nil || n.Metin == "" {
            http.Error(w, "geçersiz not", http.StatusBadRequest)
            return
        }
        d.mu.Lock()
        d.sonID++
        n.ID = d.sonID
        d.notlar = append(d.notlar, n)
        d.mu.Unlock()
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(http.StatusCreated)
        json.NewEncoder(w).Encode(n)
    })
    mux.HandleFunc("GET /notlar", func(w http.ResponseWriter, r *http.Request) {
        d.mu.Lock()
        defer d.mu.Unlock()
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(d.notlar)
    })

    mux.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest("POST", "/notlar", strings.NewReader(`{"metin":"süt al"}`)))
    rec := httptest.NewRecorder()
    mux.ServeHTTP(rec, httptest.NewRequest("GET", "/notlar", nil))
    fmt.Print(rec.Body.String())
}

Expected output

[{"id":1,"metin":"süt al"}]

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 If you want the GET response to be <code>[]</code> instead of <code>null</code> when the list is empty, what do you do?

  • Add omitempty
  • Write the response as a string
  • It isn't possible
  • Initialize the slice with []Not{} Correct answer

Explanation: A nil slice encodes as null in JSON; an empty non-nil slice encodes as [].

2 Created code

Question
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.____)
json.NewEncoder(w).Encode(n)
Answer
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(n)

Explanation: 201 Created is returned when a new resource is added.

3 Request and response

Term Meaning
POST with valid text 201 + note
POST with empty text 400
GET 200 + array
PUT /notlar 405

All lessons in Intermediate