前回まででコメントの格納ができるようになった(っぽい)ので、今度はそれを取得できるようにして実際入ってることを確認する。
コメント取得用のAPI
まずはコメント取得用のAPIを作るところから考える。投稿用のAPIと同じ形を踏襲するので、特に深く考えることなく以下のURLにする。
http://localhost:8080/1/remark/get
あとはここのURLに対してのリクエストをハンドルする処理をサーバーサイドに追加していく。
サーバーサイド
そろそろ長くなってきたので、差分を載せていくことにする。下記がコメントの取得用APIを追加した部分のコード。
...
func init() {
http.HandleFunc("/", errorHandler(root))
http.HandleFunc("/1/remark/post", errorHandler(postRemark))
http.HandleFunc("/1/remark/get", errorHandler(getRemark))
}
...
// Handle a get request of remarks
func getRemark(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
switch r.Method {
case GET:
c.Infof("Retrieving Remarks\n")
// get all the remarks (new remarks come first)
query := datastore.NewQuery(REMARK_KIND).Order("-Time")
itr := query.Run(c)
buf := bytes.NewBuffer(nil) // a variable sized buffer
for {
var remark Remark
key, err := itr.Next(&remark)
if err == datastore.Done {
break
} else if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError)
break
}
c.Infof("Retrieved id:%d remark:%s", key.IntID(), remark.Content)
fmt.Fprintf(buf, "id=%d&remark=%s\n", remark.Time, remark.Content)
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
io.Copy(w, buf)
default:
msg := "Invalid request\n"
c.Infof(msg)
http.Error(w, msg, http.StatusBadRequest)
}
}
処理の流れは以下の通り。重要なのは主にクエリをどう生成するか、という部分だろうか。
- コメント取得用のクエリを生成する
- クエリを実行して返ってきた結果を走査しつつ、レスポンス用にデータを整形する
- 適切なヘッダを付加してレスポンスを送り返す
ちょっとわかりづらいのはOrder("-Time")という部分で、ここは取得したデータのうち、Timeというフィールドの値の大小に応じて並びかえろと指定している。TimeというフィールドはRemark構造体に自分で勝手に定義したフィールドだから、別にここに来るフィールド名自体は何でもいい。あと、デフォルトでは昇順で動くので、頭にマイナス記号をつけて降順にしている。こうすることで、時間的には新しいコメントが先にくるよう並びかえられる、というわけ。
あと今は特に引数をあたえることなく決まった動作(全部のコメントを新しい順に取得する)しかしてないけど、getのAPI的にはもっと細かな制御ができるようにしないといけない。でもそれはひとまずあとまわし。
クライアントサイド
引き続きクライアントサイドのコード。こっちはまだ短いので全部載せる。
package main
import (
"http"
"fmt"
"os"
)
func main() {
client := new(http.Client)
remarkUrlRoot := "http://localhost:8080/1/remark/"
getUrl := remarkUrlRoot + "get"
r, err := client.Get(getUrl)
if err != nil {
fmt.Printf("Error: %s",err)
return
}
fmt.Println(r.Status)
bufSize := 256
buf := make([]byte, bufSize)
for {
read, err := r.Body.Read(buf)
if read == 0 && err == os.EOF {
fmt.Println("Finished reading")
break
} else if err != nil {
fmt.Printf("Error during read: %s",err)
break
}
// convert the buffer to a string and print it
fmt.Println(string(buf[0:read]))
}
r.Body.Close()
}
指定されたURLにGetを発行して、返ってきたResponseのBodyを読みつつ表示する、という処理をやっている。決まりきった動作なので特に説明するところは無さそう。Goではこうやるんですよー的な見本かな。
出力結果
サーバーサイドとクライアントサイドの出力結果をそれぞれのせておく。ちゃんとクエリ通りの順番で取得できていることが確認できる。
サーバーサイドの出力結果
INFO 2011-06-26 15:19:20,629 __init__.py:324] building _go_app INFO 2011-06-26 15:19:21,554 __init__.py:316] running _go_app 2011/06/26 15:19:21 INFO: Retrieving Remarks 2011/06/26 15:19:21 INFO: Retrieved id:1309056182 remark:aardvark 2011/06/26 15:19:21 INFO: Retrieved id:1309056155 remark:aardvark 2011/06/26 15:19:21 INFO: Retrieved id:1308993994 remark:aardvark 2011/06/26 15:19:21 INFO: Retrieved id:1308993976 remark:aardvark INFO 2011-06-26 15:19:21,671 dev_appserver.py:4217] "GET /1/remark/get HTTP/1.1" 200 -
クライアントサイドの出力結果
200 OK id=1309056182000000&remark=aardvark id=1309056155000000&remark=aardvark id=1308993994000000&remark=aardvark id=1308993976000000&remark=aardvark Finished reading
次回
PostしてGetするところまではできたから、残るは(REST的には)DeleteとPut。ということで、次はたぶん投稿したコメントを削除するDeleteのAPIをやるかな。
(続く)
0 件のコメント:
コメントを投稿