go-json-restモジュールを使って簡単にAPIサーバを作る

## リポジトリ
https://github.com/ant0ine/go-json-rest

## Hello worldを表示するプログラム

```main.go
package main

import (
        "github.com/ant0ine/go-json-rest/rest"
        "log"
        "net/http"
)

func main() {
        api := rest.NewApi()
        api.Use(rest.DefaultDevStack...)
        api.SetApp(rest.AppSimple(func(w rest.ResponseWriter, r *rest.Request) {
                w.WriteJson(map[string]string{"Body": "Hello World!"})
        }))
        log.Fatal(http.ListenAndServe(":8080", api.MakeHandler()))
}
```

```
yuta:~ $ curl -i http://127.0.0.1:8080/
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-Powered-By: go-json-rest
Date: Sun, 01 Nov 2020 00:54:41 GMT
Content-Length: 28

{
  "Body": "Hello World!"
}yuta:~ $ 
```

## POST,GET,DELETE

```main.go
package main

import (
        "github.com/ant0ine/go-json-rest/rest"
        "log"
        "net/http"
        "sync"
)

func main() {
        api := rest.NewApi()
        api.Use(rest.DefaultDevStack...)
        router, err := rest.MakeRouter(
                rest.Get("/countries", GetAllCountries),
                rest.Post("/countries", PostCountry),
                rest.Get("/countries/:code", GetCountry),
                rest.Delete("/countries/:code", DeleteCountry),
        )
        if err != nil {
                log.Fatal(err)
        }
        api.SetApp(router)
        log.Fatal(http.ListenAndServe(":8080", api.MakeHandler()))
}

type Country struct {
        Code string
        Name string
}

var store = map[string]*Country{}

var lock = sync.RWMutex{}

func GetCountry(w rest.ResponseWriter, r *rest.Request) {
        code := r.PathParam("code")

        lock.RLock()
        var country *Country
        if store[code] != nil {
                country = &Country{}
                *country = *store[code]
        }
        lock.RUnlock()

        if country == nil {
                rest.NotFound(w, r)
                return
        }
        w.WriteJson(country)
}

func GetAllCountries(w rest.ResponseWriter, r *rest.Request) {
        lock.RLock()
        countries := make([]Country, len(store))
        i := 0
        for _, country := range store {
                countries[i] = *country
                i++
        }
        lock.RUnlock()
        w.WriteJson(&countries)
}

func PostCountry(w rest.ResponseWriter, r *rest.Request) {
        country := Country{}
        err := r.DecodeJsonPayload(&country)
        if err != nil {
                rest.Error(w, err.Error(), http.StatusInternalServerError)
                return
        }
        if country.Code == "" {
                rest.Error(w, "country code required", 400)
                return
        }
        if country.Name == "" {
                rest.Error(w, "country name required", 400)
                return
        }
        lock.Lock()
        store[country.Code] = &country
        lock.Unlock()
        w.WriteJson(&country)
}

func DeleteCountry(w rest.ResponseWriter, r *rest.Request) {
        code := r.PathParam("code")
        lock.Lock()
        delete(store, code)
        lock.Unlock()
        w.WriteHeader(http.StatusOK)
}
```

### POST

```
yuta:~ $ curl -i -H 'Content-Type: application/json' \
>     -d '{"Code":"FR","Name":"France"}' http://127.0.0.1:8080/countries
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-Powered-By: go-json-rest
Date: Sun, 01 Nov 2020 00:59:38 GMT
Content-Length: 38

{
  "Code": "FR",
  "Name": "France"
}yuta:~ $ 
```

### GET

```
yuta:~ $ curl -i http://127.0.0.1:8080/countries/FR
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-Powered-By: go-json-rest
Date: Sun, 01 Nov 2020 01:01:02 GMT
Content-Length: 38

{
  "Code": "FR",
  "Name": "France"
}yuta:~ $ 
```

### DELETE

```
yuta:~ $ curl -i -X DELETE http://127.0.0.1:8080/countries/FR
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-Powered-By: go-json-rest
Date: Sun, 01 Nov 2020 01:01:35 GMT
Content-Length: 0

yuta:~ $ curl -i http://127.0.0.1:8080/countries/FR
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
X-Powered-By: go-json-rest
Date: Sun, 01 Nov 2020 01:01:41 GMT
Content-Length: 35

{
  "Error": "Resource not found"
}yuta:~ $ 
yuta:~ $ ^C
```