🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

REST

REST(Representational State Transfer)はwebアプリケーションの構築スタイルの一種です。HTTP GET/POSTによってリクエストを送信し、レスポンスはXMLで返されます。SOAPのようなRPCの構築と比べるとサーバからクライアントを分離することが出来る為、人気です。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

Q&A

解決済

3回答

1021閲覧

Go言語でのRest APIの実装

anonyrabbit

総合スコア78

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

REST

REST(Representational State Transfer)はwebアプリケーションの構築スタイルの一種です。HTTP GET/POSTによってリクエストを送信し、レスポンスはXMLで返されます。SOAPのようなRPCの構築と比べるとサーバからクライアントを分離することが出来る為、人気です。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

0グッド

1クリップ

投稿2019/09/22 06:18

編集2019/09/23 08:50

ご閲覧いただきありがとうございます。

##やりたいこと

Go言語で、標準パッケージのみでRest APIを実装出来るようになりたいです。
しかし、ネットにある情報ではGorilla muxを使ったものばかりで参考になりません。

##やってみたこと

自分でも以下のように実装してみたのですが、これだとgorilla muxを使っていないのでHTTPリクエストメソッドと自前で用意したメソッドを関連づけられません。

Go

1package main 2 3import ( 4 "fmt" 5 "net/http" 6) 7 8func homePage(w http.ResponseWriter, r *http.Request) { 9 fmt.Fprintf(w, "home page hit") 10} 11 12func main() { 13 // handleRequest() 14 http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { 15 fmt.Fprintf(w, "Hello,") //Gorilla muxを使えば、ここに .Methods("GET")というようにHTTPメソッドを紐づけられる 16 }) 17 http.ListenAndServe(":8080", nil) 18}

Gorilla muxを使った実装はこちらです。これでやりたいことは実現できているのですが、標準パッケージのみで出来るようになりたいです。

Go

1package main 2 3import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 9 "github.com/gorilla/mux" 10) 11 12func homeLink(w http.ResponseWriter, r *http.Request) { 13 fmt.Fprintf(w, "Welcome home!") 14} 15 16func main() { 17 router := mux.NewRouter().StrictSlash(true) 18 router.HandleFunc("/", homeLink) 19 router.HandleFunc("/events", getAllEvents).Methods("GET") 20 router.HandleFunc("/events/{id}", getOneEvent).Methods("GET") 21 22 http.ListenAndServe(":8080", router) 23} 24 25type event struct { 26 ID string `json:"ID"` 27 Title string `json:"Title"` 28 Description string `json:"Description"` 29} 30 31type allEvents []event 32 33var events = allEvents{ 34 { 35 ID: "11", 36 Title: "Introduction to Golang", 37 Description: "Come join us for a chance to learn how golang works and get to eventually try it out", 38 }, 39} 40 41func getOneEvent(w http.ResponseWriter, r *http.Request) { 42 eventID := mux.Vars(r)["id"] 43 44 for _, singleEvent := range events { 45 if singleEvent.ID == eventID { 46 json.NewEncoder(w).Encode(singleEvent) 47 } 48 } 49} 50 51func getAllEvents(w http.ResponseWriter, r *http.Request) { 52 json.NewEncoder(w).Encode(events) 53} 54

もしお分かりになる方がいましたら、どうかお力をお貸しください。よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

要約するとルーターを標準パッケージでどうつくるかという質問ですね。

提示された例の程度であればほとんど標準のルーターで十分にまかなえます。

go

1func main() { 2 mux := http.NewServeMux() 3 mux.HandleFunc("/", homeLink) 4 mux.HandleFunc("/events", getAllEvents) 5 mux.Handle("/events/", http.StripPrefix("/events/", http.HandlerFunc(getOneEvent))) 6 7 http.ListenAndServe(":8080", mux) 8}

{id}を取り出す部分は以下のコードで取り出せます。

go

1func getOneEvent(w http.ResponseWriter, r *http.Request) { 2 eventID := r.URL.Path 3 ... 4}

ポイントは予めhttp.StripPrefixでURLの先頭を削っておいたところですね。

goではhttpハンドリングに使える型に以下の2種類があります。

  • 「Handler」型(ServeHTTPメソッドをもつ)
  • 「func (w http.ResponseWriter, r *http.Request)」関数型

「func (w http.ResponseWriter, r *http.Request)」関数型は
HandlerFunc型にキャストすればHandlerとコンパチになることを覚えておくと良いでしょう。

いろんなミドルウェアはHandlerをラップしてHandlerを形成するパターンが多いので
Handler互換であればいろんなミドルウェアが使えます。
そのひとつが「http.StripPrefix」です。

投稿2019/09/22 22:37

編集2019/09/22 22:41
nobonobo

総合スコア3367

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nobonobo

2019/09/22 23:00

同様にGET以外のメソッドをエラーにするミドルウェアを書いてハンドラをラップしておくと完全に互換のコードになりますので挑戦してみてください。
nobonobo

2019/09/24 01:07

gorillaのmuxの「.Methods("GET")」というのは別に紐づけるとかではなく、Methodsに指定したメソッド以外をhttp.StatusMethodNotAllowedステータスを返すということです。 そのような処理をハンドラ側に書いてもいいし、ミドルウェアでそのような処理を挟んでもいいわけです。 やれる方法で書いてみていただけたらアドバイスします。
nobonobo

2019/09/24 01:08

http.RequestにはMethodというフィールドがあります。そこをチェックするだけなので頑張ってみましょう。
anonyrabbit

2019/09/25 10:33

丁寧なご回答ありがとうございます。自分のやり方を記入致しますので、アドバイスいただけると非常にありがたいです。
guest

0

ベストアンサー

上記回答例のように振り分け用にハンドラを一つ挟むやり方は最もオススメです。
応用が一番効くので。GETだけに反応させたければ以下のように書きましょう。
もちろんGETとPOSTそれぞれに違う処理を入れるのはアリです。

go

1func handleTrustRequest(w http.ResponseWriter, r *http.Request) { 2 switch r.Method { 3 case "GET": 4 handleTrustGet(w, r) 5 default: 6 http.Error(w, r.Method + " method not allowed", http.StatusMethodNotAllowed) 7 } 8}

エラーを上記のように書いた場合、クライアントに届けるエラーのコンテンツがテキストになります。
JSONである必要があればContent-Typeにapplication/jsonを指定してから
JSONを文字列に変換したものを返すなどしましょう。

で、ミドルウェア方式は以下のように書きます。

go

1func GetOnly(next http.Handler) http.Handler { 2 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3 if r.Method != "GET" { 4 http.Error(w, r.Method + " method not allowed", http.StatusMethodNotAllowed) 5 } 6 next.ServeHTTP(w, r) 7 }) 8} 9 10func main() { 11 http.Handle("/trust", GetOnly(http.HandlerFunc(handleTrustGet))) 12}

上記の例だとじゃぁPOSTの対応を増やすには?というと結局分岐を挟まないといけなくなって、
ミドルウェアの体裁で分岐を書くのはちょっと骨が折れます。なので質問者さんの回答の書き方を
基本に書いておくと良いと思います。(標準だけでMethod別に振り分けする方法としてはベストじゃないかと)

投稿2019/09/25 13:42

nobonobo

総合スコア3367

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

anonyrabbit

2019/09/26 03:01

丁寧なご回答ありがとうございます。コードを確認していただけて、サンプルコードまで提示していただけて、とても助かりました。ありがとうございました。
anonyrabbit

2019/09/26 03:06

それと、あと一つだけ教えていただきたいのですが、なぜ二つ目のコードはミドルウェア方式になるのでしょうか? ミドルウェアとは、「オペレーションシステム(OS)」と「アプリケーションソフト」の中間に入るソフトウェアのことだと思うのですが、2つ目のどこにコードにミドルウェア要素があるのかが分かりません・・・。 参考になるリンクだけ提示していただくのでも良いので、簡単に教えていただけると助かります。
anonyrabbit

2019/09/26 08:46

なるほど、それは知りませんでした。ありがとうございます。覚えておきます。
guest

0

nobonobo様

ここではベタ書きしたJSON形式の構造体のデータを使っていますが、一応リソースとURIを対応づけていますし、HTTPリクエストメソッドを通して内部処理をしているので、これだけでRest APIになっていると言えると思うのですが、いかがでしょうか?改善のためアドバイス頂けますと幸いです。

Go

1 2func main() { 3 4 http.HandleFunc("/trust", handleTrustRequest 5 http.HandleFunc("/belief", handleBeliefRequest) 6 http.ListenAndServe(":8080", nil) 7} 8 9func handleTrustRequest(w http.ResponseWriter, r *http.Request) { 10 switch r.Method { 11 case "GET": 12 handleTrustGet(w, r) 13 case "POST": 14 15 } 16} 17 18type trust struct { 19 ID string `json:"ID"` 20 Title string `json:"Title"` 21 Description string `json:"Description"` 22} 23 24type allTrusts []trust 25 26var trusts = allTrusts{ 27 { 28 ID: "11", 29 Title: "trust", 30 Description: "Come join us for a chance to learn how golang works and get to eventually try it out", 31 }, 32} 33 34func handleTrustGet(w http.ResponseWriter, r *http.Request) { 35 json.NewEncoder(w).Encode(trusts) 36} 37

投稿2019/09/25 10:41

anonyrabbit

総合スコア78

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問