forked from Mirrors/jsonapi
readme work and an example app
This commit is contained in:
parent
b112561a72
commit
ffcbb660c3
101
README.md
101
README.md
|
@ -18,4 +18,105 @@ supports relationships out of the box and will even side load them in
|
|||
your response into an "included" array--that contains associated
|
||||
objects.
|
||||
|
||||
## Introduction
|
||||
|
||||
jsonapi uses StructField tags to annotate the structs fields that you
|
||||
already have and use in your app and then reads and writes jsonapi.org
|
||||
output based on the instructions you give the library in your jsonapi
|
||||
tags. Let's take an example. In your app,
|
||||
you most likely have structs that look similar to these,
|
||||
|
||||
|
||||
```go
|
||||
type Blog struct {
|
||||
Id int `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Posts []*Post `json:"posts"`
|
||||
CurrentPost *Post `json:"current_post"`
|
||||
CurrentPostId int `json:"current_post_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ViewCount int `json:"view_count"`
|
||||
}
|
||||
|
||||
type Post struct {
|
||||
Id int `json:"id"`
|
||||
BlogId int `json:"blog_id"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
Comments []*Comment `json:"comments"`
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
Id int `json:"id"`
|
||||
PostId int `json:"post_id"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
```
|
||||
|
||||
These structs may or may not resemble the layout of your database. But
|
||||
these are the ones that you want to use right? You wouldn't want to use
|
||||
structs like those that jsonapi sends because it is very hard to get at all of
|
||||
your data easily.
|
||||
|
||||
|
||||
## Tags Example
|
||||
|
||||
You want jsonapi.org style inputs and ouputs but you want to keep your
|
||||
structs that you already have. Use the jsonapi lib with the "jsonapi"
|
||||
tag on your struct fields along with its Marshal and Unmarshal methods
|
||||
to construct and read your responses and replies, respectively. Here's
|
||||
an example of the structs above using jsonapi tags,
|
||||
|
||||
```go
|
||||
type Blog struct {
|
||||
Id int `jsonapi:"primary,blogs"`
|
||||
Title string `jsonapi:"attr,title"`
|
||||
Posts []*Post `jsonapi:"relation,posts"`
|
||||
CurrentPost *Post `jsonapi:"relation,current_post"`
|
||||
CurrentPostId int `jsonapi:"attr,current_post_id"`
|
||||
CreatedAt time.Time `jsonapi:"attr,created_at"`
|
||||
ViewCount int `jsonapi:"attr,view_count"`
|
||||
}
|
||||
|
||||
type Post struct {
|
||||
Id int `jsonapi:"primary,posts"`
|
||||
BlogId int `jsonapi:"attr,blog_id"`
|
||||
Title string `jsonapi:"attr,title"`
|
||||
Body string `jsonapi:"attr,body"`
|
||||
Comments []*Comment `jsonapi:"relation,comments"`
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
Id int `jsonapi:"primary,comments"`
|
||||
PostId int `jsonapi:"attr,post_id"`
|
||||
Body string `jsonapi:"attr,body"`
|
||||
}
|
||||
```
|
||||
|
||||
## Handler Examples
|
||||
|
||||
Now you have your structs prepared to be seralized or materialized.
|
||||
What about the rest?
|
||||
|
||||
### Create
|
||||
|
||||
```go
|
||||
func CreateBlog(w http.ResponseWriter, r *http.Request) {
|
||||
blog := new(Blog)
|
||||
|
||||
if err := jsonapi.UnmarshalPayload(r.Body, blog); err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
||||
// ...do stuff with your blog...
|
||||
|
||||
w.WriteHeader(201)
|
||||
w.Header().Set("Content-Type", "application/vnd.api+json")
|
||||
|
||||
if err := jsonapi.MarshalOnePayload(w, blog); err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/shwoodard/jsonapi"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/blogs", func(w http.ResponseWriter, r *http.Request) {
|
||||
if !regexp.MustCompile(`application/vnd\.api\+json`).Match([]byte(r.Header.Get("Accept"))) {
|
||||
http.Error(w, "Not Acceptable", 406)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method == "POST" {
|
||||
createBlog(w, r)
|
||||
} else {
|
||||
listBlogs(w, r)
|
||||
}
|
||||
})
|
||||
|
||||
blog := testBlogForCreate()
|
||||
payload, _ := jsonapi.MarshalOnePayloadEmbedded(blog)
|
||||
|
||||
in := bytes.NewBuffer(nil)
|
||||
json.NewEncoder(in).Encode(payload)
|
||||
|
||||
req, _ := http.NewRequest("POST", "/blogs", in)
|
||||
|
||||
req.Header.Set("Accept", "application/vnd.api+json")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
http.DefaultServeMux.ServeHTTP(w, req)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
io.Copy(buf, w.Body)
|
||||
|
||||
fmt.Println("============ jsonapi response from create ===========\n")
|
||||
fmt.Println(buf.String())
|
||||
fmt.Println("============== end raw jsonapi response =============")
|
||||
|
||||
responseBlog := new(Blog)
|
||||
|
||||
jsonapi.UnmarshalPayload(buf, responseBlog)
|
||||
|
||||
out := bytes.NewBuffer(nil)
|
||||
json.NewEncoder(out).Encode(responseBlog)
|
||||
|
||||
fmt.Println("\n================ Viola! Converted back our Blog struct =================\n")
|
||||
fmt.Printf("%s\n", out.Bytes())
|
||||
fmt.Println("================ end marshal materialized Blog struct =================")
|
||||
}
|
||||
|
||||
func createBlog(w http.ResponseWriter, r *http.Request) {
|
||||
blog := new(Blog)
|
||||
|
||||
if err := jsonapi.UnmarshalPayload(r.Body, blog); err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
||||
// ...do stuff with your blog...
|
||||
|
||||
w.WriteHeader(201)
|
||||
w.Header().Set("Content-Type", "application/vnd.api+json")
|
||||
|
||||
if err := jsonapi.MarshalOnePayload(w, blog); err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
}
|
||||
}
|
||||
|
||||
func listBlogs(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
type Blog struct {
|
||||
Id int `jsonapi:"primary,blogs"`
|
||||
Title string `jsonapi:"attr,title"`
|
||||
Posts []*Post `jsonapi:"relation,posts"`
|
||||
CurrentPost *Post `jsonapi:"relation,current_post"`
|
||||
CurrentPostId int `jsonapi:"attr,current_post_id"`
|
||||
CreatedAt time.Time `jsonapi:"attr,created_at"`
|
||||
ViewCount int `jsonapi:"attr,view_count"`
|
||||
}
|
||||
|
||||
type Post struct {
|
||||
Id int `jsonapi:"primary,posts"`
|
||||
BlogId int `jsonapi:"attr,blog_id"`
|
||||
Title string `jsonapi:"attr,title"`
|
||||
Body string `jsonapi:"attr,body"`
|
||||
Comments []*Comment `jsonapi:"relation,comments"`
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
Id int `jsonapi:"primary,comments"`
|
||||
PostId int `jsonapi:"attr,post_id"`
|
||||
Body string `jsonapi:"attr,body"`
|
||||
}
|
||||
|
||||
func testBlogForCreate() *Blog {
|
||||
return &Blog{
|
||||
Id: 1,
|
||||
Title: "Title 1",
|
||||
CreatedAt: time.Now(),
|
||||
Posts: []*Post{
|
||||
&Post{
|
||||
Id: 1,
|
||||
Title: "Foo",
|
||||
Body: "Bar",
|
||||
Comments: []*Comment{
|
||||
&Comment{
|
||||
Id: 1,
|
||||
Body: "foo",
|
||||
},
|
||||
&Comment{
|
||||
Id: 2,
|
||||
Body: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
&Post{
|
||||
Id: 2,
|
||||
Title: "Fuubar",
|
||||
Body: "Bas",
|
||||
Comments: []*Comment{
|
||||
&Comment{
|
||||
Id: 1,
|
||||
Body: "foo",
|
||||
},
|
||||
&Comment{
|
||||
Id: 3,
|
||||
Body: "bas",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
CurrentPost: &Post{
|
||||
Id: 1,
|
||||
Title: "Foo",
|
||||
Body: "Bar",
|
||||
Comments: []*Comment{
|
||||
&Comment{
|
||||
Id: 1,
|
||||
Body: "foo",
|
||||
},
|
||||
&Comment{
|
||||
Id: 2,
|
||||
Body: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -8,27 +8,30 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
type Comment struct {
|
||||
Id int `jsonapi:"primary,comments"`
|
||||
Body string `jsonapi:"attr,body"`
|
||||
}
|
||||
|
||||
type Post struct {
|
||||
Id int `jsonapi:"primary,posts"`
|
||||
Title string `jsonapi:"attr,title"`
|
||||
Body string `jsonapi:"attr,body"`
|
||||
Comments []*Comment `jsonapi:"relation,comments"`
|
||||
}
|
||||
|
||||
type Blog struct {
|
||||
Id int `jsonapi:"primary,blogs"`
|
||||
Title string `jsonapi:"attr,title"`
|
||||
Posts []*Post `jsonapi:"relation,posts"`
|
||||
CurrentPost *Post `jsonapi:"relation,current_post"`
|
||||
CurrentPostId int `jsonapi:"attr,current_post_id"`
|
||||
CreatedAt time.Time `jsonapi:"attr,created_at"`
|
||||
ViewCount int `jsonapi:"attr,view_count"`
|
||||
}
|
||||
|
||||
type Post struct {
|
||||
Id int `jsonapi:"primary,posts"`
|
||||
BlogId int `jsonapi:"attr,blog_id"`
|
||||
Title string `jsonapi:"attr,title"`
|
||||
Body string `jsonapi:"attr,body"`
|
||||
Comments []*Comment `jsonapi:"relation,comments"`
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
Id int `jsonapi:"primary,comments"`
|
||||
PostId int `jsonapi:"attr,post_id"`
|
||||
Body string `jsonapi:"attr,body"`
|
||||
}
|
||||
|
||||
type Blogs []*Blog
|
||||
|
||||
func (b Blogs) GetData() []interface{} {
|
||||
|
|
Loading…
Reference in New Issue