Last active
March 13, 2016 13:34
-
-
Save sgykfjsm/1dd9a8eee1f70a7068c9 to your computer and use it in GitHub Desktop.
simple json api by Go version2. This code is based on http://thenewstack.io/make-a-restful-json-api-go/ and http://openmymind.net/Go-action-responses/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
for i in $(seq 1 3) | |
do | |
curl --silent -XGET "localhost:8080/todos/${i}" \ | |
--write-out '\nGET, %{http_code}, %{content_type}\n' | |
sleep 1 | |
done | |
curl --silent -XGET "localhost:8080/todos/foo" \ | |
--write-out '\nGET, %{http_code}, %{content_type}\n' | |
curl --silent \ | |
-XPOST \ | |
-H "Content-Type: application/json" -d '{"name":"New Todo"}' \ | |
"localhost:8080/todos" \ | |
--write-out '\nPOST, %{http_code}, %{content_type}\n' | |
sleep 1 | |
curl --silent -XGET "localhost:8080/todos/3" \ | |
--output /dev/null \ | |
--write-out '\nGET, %{http_code}, %{content_type}\n' | |
curl --silent \ | |
-XDELETE \ | |
"localhost:8080/todos/3" \ | |
--write-out '\nDELETE, %{http_code}, %{content_type}\n' | |
curl --silent -XGET "localhost:8080/todos/3" \ | |
--write-out '\nGET, %{http_code}, %{content_type}\n' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ bash ./curl.sh | |
* Trying ::1... | |
* Connected to localhost (::1) port 8080 (#0) | |
> GET /todos/1 HTTP/1.1 | |
> Host: localhost:8080 | |
> User-Agent: curl/7.43.0 | |
> Accept: */* | |
> | |
< HTTP/1.1 200 OK | |
< Content-Type: application/json | |
< Date: Sun, 13 Mar 2016 13:29:29 GMT | |
< Content-Length: 83 | |
< | |
* Connection #0 to host localhost left intact | |
{"id":1,"name":"Write presentation","completed":false,"due":"0001-01-01T00:00:00Z"} | |
GET, 200, application/json | |
--------------------------- | |
* Trying ::1... | |
* Connected to localhost (::1) port 8080 (#0) | |
> GET /todos/2 HTTP/1.1 | |
> Host: localhost:8080 | |
> User-Agent: curl/7.43.0 | |
> Accept: */* | |
> | |
< HTTP/1.1 200 OK | |
< Content-Type: application/json | |
< Date: Sun, 13 Mar 2016 13:29:30 GMT | |
< Content-Length: 76 | |
< | |
* Connection #0 to host localhost left intact | |
{"id":2,"name":"Host meetup","completed":false,"due":"0001-01-01T00:00:00Z"} | |
GET, 200, application/json | |
--------------------------- | |
* Trying ::1... | |
* Connected to localhost (::1) port 8080 (#0) | |
> GET /todos/3 HTTP/1.1 | |
> Host: localhost:8080 | |
> User-Agent: curl/7.43.0 | |
> Accept: */* | |
> | |
< HTTP/1.1 404 Not Found | |
< Date: Sun, 13 Mar 2016 13:29:31 GMT | |
< Content-Length: 4 | |
< Content-Type: text/plain; charset=utf-8 | |
< | |
* Connection #0 to host localhost left intact | |
null | |
GET, 404, text/plain; charset=utf-8 | |
--------------------------- | |
* Trying ::1... | |
* Connected to localhost (::1) port 8080 (#0) | |
> GET /todos/foo HTTP/1.1 | |
> Host: localhost:8080 | |
> User-Agent: curl/7.43.0 | |
> Accept: */* | |
> | |
< HTTP/1.1 422 status code 422 | |
< Content-Type: application/json | |
< Date: Sun, 13 Mar 2016 13:29:32 GMT | |
< Content-Length: 23 | |
< | |
* Connection #0 to host localhost left intact | |
todoId should be number | |
GET, 422, application/json | |
--------------------------- | |
* Trying ::1... | |
* Connected to localhost (::1) port 8080 (#0) | |
> POST /todos HTTP/1.1 | |
> Host: localhost:8080 | |
> User-Agent: curl/7.43.0 | |
> Accept: */* | |
> Content-Type: application/json | |
> Content-Length: 19 | |
> | |
* upload completely sent off: 19 out of 19 bytes | |
< HTTP/1.1 201 Created | |
< Content-Type: application/json | |
< Location: http://localhost:8080//todos/3 | |
< Date: Sun, 13 Mar 2016 13:29:32 GMT | |
< Content-Length: 73 | |
< | |
* Connection #0 to host localhost left intact | |
{"id":3,"name":"New Todo","completed":false,"due":"0001-01-01T00:00:00Z"} | |
POST, 201, application/json | |
--------------------------- | |
* Trying ::1... | |
* Connected to localhost (::1) port 8080 (#0) | |
> GET /todos/3 HTTP/1.1 | |
> Host: localhost:8080 | |
> User-Agent: curl/7.43.0 | |
> Accept: */* | |
> | |
< HTTP/1.1 200 OK | |
< Content-Type: application/json | |
< Date: Sun, 13 Mar 2016 13:29:33 GMT | |
< Content-Length: 73 | |
< | |
{ [73 bytes data] | |
* Connection #0 to host localhost left intact | |
GET, 200, application/json | |
--------------------------- | |
* Trying ::1... | |
* Connected to localhost (::1) port 8080 (#0) | |
> DELETE /todos/3 HTTP/1.1 | |
> Host: localhost:8080 | |
> User-Agent: curl/7.43.0 | |
> Accept: */* | |
> | |
< HTTP/1.1 204 No Content | |
< Date: Sun, 13 Mar 2016 13:29:34 GMT | |
< | |
* Connection #0 to host localhost left intact | |
DELETE, 204, | |
--------------------------- | |
* Trying ::1... | |
* Connected to localhost (::1) port 8080 (#0) | |
> GET /todos/3 HTTP/1.1 | |
> Host: localhost:8080 | |
> User-Agent: curl/7.43.0 | |
> Accept: */* | |
> | |
< HTTP/1.1 404 Not Found | |
< Date: Sun, 13 Mar 2016 13:29:34 GMT | |
< Content-Length: 4 | |
< Content-Type: text/plain; charset=utf-8 | |
< | |
* Connection #0 to host localhost left intact | |
null | |
GET, 404, text/plain; charset=utf-8 | |
--------------------------- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"net/http" | |
"strconv" | |
"github.com/gorilla/mux" | |
) | |
type MyHandle func(*http.Request) Response | |
func IDShouldBeInt(h func(r *http.Request) Response, name string) MyHandle { | |
return Logging(func(r *http.Request) Response { | |
_, err := strconv.Atoi(mux.Vars(r)["todoId"]) | |
if err != nil { | |
return Error(422, "todoId should be number", err) | |
} | |
return h(r) | |
}, name) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"fmt" | |
"io" | |
"io/ioutil" | |
"net/http" | |
"strconv" | |
"github.com/gorilla/mux" | |
) | |
func Index(r *http.Request) Response { | |
return Respond(http.StatusOK, "Welcmoe") | |
} | |
func TodoIndex(r *http.Request) Response { | |
return Json(http.StatusOK, todos) | |
} | |
func TodoShow(r *http.Request) Response { | |
id, _ := strconv.Atoi(mux.Vars(r)["todoId"]) | |
t := RepoFindTodo(id) | |
if t.ID == 0 && t.Name == "" { | |
return Empty(http.StatusNotFound) | |
} | |
return Json(http.StatusOK, t) | |
} | |
func TodoCreate(r *http.Request) Response { | |
var todo Todo | |
body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576)) // 1MiB | |
if err != nil { | |
return Error(http.StatusInternalServerError, "request body is too large", err) | |
} | |
defer r.Body.Close() | |
if err := json.Unmarshal(body, &todo); err != nil { | |
return Error(http.StatusInternalServerError, "failed marshalling json", err) | |
} | |
t := RepoCreateTodo(todo) | |
location := fmt.Sprintf("http://%s/%s/%d", r.Host, r.URL.Path, t.ID) | |
return Created(http.StatusCreated, t, location) | |
} | |
func TodoDelete(r *http.Request) Response { | |
id, _ := strconv.Atoi(mux.Vars(r)["todoId"]) | |
if err := RepoDestroyTodo(id); err != nil { | |
return Empty(http.StatusNotFound) | |
} | |
return Empty(204) // 204 No Content | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"log" | |
"net/http" | |
"time" | |
) | |
var logger = func(method, uri, name string, status int, start time.Time) { | |
log.Printf("\"method\":%q \"uri\":%q \"name\":%q \"status\":%d \"time\":%q", method, uri, name, status, time.Since(start)) | |
} | |
func Logging(h func(r *http.Request) Response, name string) MyHandle { | |
return func(r *http.Request) Response { | |
start := time.Now() | |
result := h(r) | |
logger(r.Method, r.URL.Path, name, result.Status(), start) | |
return result | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"log" | |
"net/http" | |
"github.com/gorilla/mux" | |
) | |
func decorator(h func(r *http.Request) Response) func(http.ResponseWriter, *http.Request) { | |
return func(w http.ResponseWriter, r *http.Request) { | |
result := h(r) | |
result.Write(w) | |
} | |
} | |
func main() { | |
r := mux.NewRouter() | |
r.HandleFunc("/", decorator(Logging(Index, "index"))).Methods("GET") | |
r.HandleFunc("/todos", decorator(Logging(TodoIndex, "todo-index"))).Methods("GET") | |
r.HandleFunc("/todos/{todoId}", decorator(IDShouldBeInt(TodoShow, "todo-show"))).Methods("GET") | |
r.HandleFunc("/todos", decorator(Logging(TodoCreate, "todo-create"))).Methods("POST") | |
r.HandleFunc("/todos/{todoId}", decorator(IDShouldBeInt(TodoDelete, "todo-delete"))).Methods("DELETE") | |
http.Handle("/", r) | |
log.Println("start") | |
log.Fatal(http.ListenAndServe(":8080", nil)) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "fmt" | |
var ( | |
todos Todos | |
currentID int | |
) | |
func init() { | |
RepoCreateTodo(Todo{Name: "Write presentation"}) | |
RepoCreateTodo(Todo{Name: "Host meetup"}) | |
} | |
func RepoFindTodo(id int) Todo { | |
for _, t := range todos { | |
if t.ID == id { | |
return t | |
} | |
} | |
return Todo{} | |
} | |
func RepoCreateTodo(t Todo) Todo { | |
currentID += 1 | |
t.ID = currentID | |
todos = append(todos, t) | |
return t | |
} | |
func RepoDestroyTodo(id int) error { | |
for i, t := range todos { | |
if t.ID == id { | |
todos = append(todos[:i], todos[i+1:]...) | |
return nil | |
} | |
} | |
return fmt.Errorf("Could not find Todo with id of %d to delete", id) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"log" | |
"net/http" | |
) | |
type Response interface { | |
Write(w http.ResponseWriter) | |
Status() int | |
} | |
type NormalResponse struct { | |
status int // HTTPステータスコードに対応する | |
body []byte // レスポンスボディに対応する | |
header http.Header // HTTPレスポンスヘッダーに対応する | |
} | |
func (r *NormalResponse) Write(w http.ResponseWriter) { | |
header := w.Header() | |
for k, v := range r.header { | |
header[k] = v | |
} | |
w.WriteHeader(r.status) | |
w.Write(r.body) | |
} | |
func (r *NormalResponse) Status() int { | |
return r.status | |
} | |
func (r *NormalResponse) Header(key, value string) *NormalResponse { | |
r.header.Set(key, value) | |
return r | |
} | |
func Empty(status int) *NormalResponse { | |
return Respond(status, nil) | |
} | |
func Json(status int, body interface{}) *NormalResponse { | |
return Respond(status, body).Header("Content-Type", "application/json") | |
} | |
func Created(status int, body interface{}, location string) *NormalResponse { | |
return Json(status, body).Header("Location", location) | |
} | |
func Error(status int, message string, err error) *NormalResponse { | |
log.Printf("%s, %s", message, err) | |
return Respond(status, message).Header("Content-Type", "application/json") | |
} | |
func Respond(status int, body interface{}) *NormalResponse { | |
var b []byte | |
var err error | |
switch t := body.(type) { | |
case string: | |
b = []byte(t) | |
default: | |
if b, err = json.Marshal(body); err != nil { | |
return Error(http.StatusInternalServerError, "failed marshalling json", err) | |
} | |
} | |
return &NormalResponse{ | |
status: status, | |
body: b, | |
header: make(http.Header), | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "time" | |
type Todo struct { | |
ID int `json:"id"` | |
Name string `json:"name"` | |
Completed bool `json:"completed"` | |
Due time.Time `json:"due"` | |
} | |
type Todos []Todo |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment