Webからエンタープライズアプリケーションまで、RESTfulというアーキテクチャーが使われています。ステートレス設計として、別々のソフトウェアコンポーネント間の通信を提供する強力な方法であります。 Web開発者として、RESTful APIを作るために、主にPythonを使いますが、最近Goプログラミング言語に出会い、興味を持ってきました。Goプログラミング言語(golangでも呼ばれる)は、Googleのプロジェクトで、マイクロサービスアーキテクチャに適当な多くの機能も提供するので、人気が増えています。
本記事、golangを使ってシンプルなRESTful APIをどう作るか説明したいと思います。
前提条件
このチュートリアルがゼロからじゃなくて、前提として読者がこの三つのポイントが持っています:
- プログラミングの経験や知識があること
- RESTful APIに関して理解
- Golang(^1.12.1)がインストールされていること
仕様
RESTful APIは、通常CRUDができます。CRUDとは、データベースの四つの主要な機能、「作成(Create)」「読み出し(Read)」「更新(Update)」「削除(Delete)」ですが、今回は、簡単にするために、読み出しの機能しか作りません。そして、データがJSONの形式で書きます。
そして、このエンドポイントがあります:
- / -> ホームページですが、とりあえずテキストしかを見せない
- /items/ -> Itemのリスト、Itemデータを全部見せる
- /items/{id} -> 特定のItemの情報
それぞれのItemは、「title」と「description」のデータがあって、簡単にするために、データベースを使用するのではなく、データを事前に定義します。
開発プロセス
最初は、必要なパッケージをインストールします。ルーティングのために、Muxというパッケージを使っていきます。CMD / Terminalを開けて、このコマンドを実行してください:
go get -u github.com/gorilla/mux
GitHubリポ:https://github.com/gorilla/mux
その後、main.go
というファイルを作成して、このコードを入れてください:
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
すべてのgolangを使って作られたプログラムがパッケージで構成されています。この部分では、何のパッケージが必要かが書いてあります。この場合には、四つのパッケージが必要です。
ルーティング
同じファイルで、さっきの書いたコードの下に、このコードを入れてください:
// Controller for the / route (home)
func homePage(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "This is the home page. Welcome!")
}
// Controller for the /items route
func returnAllItems(w http.ResponseWriter, r *http.Request) {
// Code for returning all items here
}
// Controller for the /items/{id} route
func returnSingleItem(w http.ResponseWriter, r *http.Request) {
// Code for returning a single item here
}
func handleRequests() {
myRouter := mux.NewRouter().StrictSlash(true)
myRouter.HandleFunc("/", homePage)
myRouter.HandleFunc("/items", returnAllItems)
myRouter.HandleFunc("/items/{id}", returnSingleItem)
log.Fatal(http.ListenAndServe(":8000", myRouter))
}
func main() {
handleRequests()
}
main()
という関数は、このプログラムをスタートすると、自動的に実行されます。その中に、handleRequests()
という関数が実行されます。handleRequests()
の関数で、それぞれのエンドポイントがアクセスされると、何が起こるかが書いてあります。例えば、/
のエンドポイントがアクセスされると、homePage
という関数が実行されるとか、/items
のエンドポイントがアクセスされると、returnAllItems
という関数が実行されます。
そして、このコード:
log.Fatal(http.ListenAndServe(":8000", myRouter))
の意味は、このプログラムが8000
のポートで、アクセスできます。これで、もう使えるなので、スタートしてみましょう!
CMD / Terminal で、このコマンドを実行してください:
go run main.go
ウェブブラウザ(Google Chrome、Mozilla Firefoxなど)を開けて、localhost:8000
に行くと、このテキストが見えます:
homePage()
の関数で、このテキストが見えます。でも、/items
に行くと、何もが見えません。原因は、他の関数(特にreturnAllItems)がまだ何もしません。
サポート関数
これから、二つのパッケージが必要なので、インポートしましょう。最初のimport部分をこれに変更してください:
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"github.com/gorilla/mux"
)
encoding/json
とstrconv
というパッケージを追加しました。もうすぐこの二つを使います。
それから、二つのサポート関数を書きましょう:
func respondWithError(w http.ResponseWriter, code int, msg string) {
respondWithJson(w, code, map[string]string{"error": msg})
}
func respondWithJson(w http.ResponseWriter, code int, payload interface{}) {
response, _ := json.Marshal(payload)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
w.Write(response)
}
両方の関数は、JSONの形式でHTTP requestに答える役割があります。
Itemデータ構造
Itemのデータ構造を書きましょう。importの部分の下に、このコードを入れてください:
// Item representation
type Item struct {
Title string `json:"title"`
Description string `json:"description"`
}
仕様に基づいて、それぞれのItemは、「title」と「description」のデータがあります。そして、その下に、これを入れて下さい:
// Global, static list of items
var itemList = []Item{
Item{Title: "Item A", Description: "The first item"},
Item{Title: "Item B", Description: "The second item"},
}
今回は、データベースを使わないので、これでデータを事前に定義します。二つのItemだけ書きましたが、自由にいくつでもいいですよ。
コア関数
最後に、コア関数(returnAllItemsとreturnSingleItem)を書きます。
// Controller for the /items route
func returnAllItems(w http.ResponseWriter, r *http.Request) {
respondWithJson(w, http.StatusOK, itemList)
}
基本的に、/items
のエンドポイントをアクセスすると、returnAllItems()
という関数がすべてのItemをJSONの形式で答えます。
// Controller for the /items/{id} route
func returnSingleItem(w http.ResponseWriter, r *http.Request) {
// Get query parameters using Mux
vars := mux.Vars(r)
// Convert {id} parameter from string to int
key, err := strconv.Atoi(vars["id"])
// If {id} parameter is not valid int
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid request payload")
return
}
// If Item with ID of {id} does not exist
if key >= len(itemList) {
respondWithError(w, http.StatusNotFound, "Item does not exist")
return
}
respondWithJson(w, http.StatusOK, itemList[key])
}
そして、returnSingleItem()
という関数が、id
のURLパラメーターに基づいて特定のItemの情報をJSONの形式で答えます。
例えば、localhost:8000/items/1
をアクセスしたら、1
が__id__になります。それから、その__id__は、indexとしてitemList
という配列からデータを取ってJSONの形式で答えます。__id__は1
の場合は、二つ目のデータが取られます。そして、__id__は0
の場合は、一つ目のデータが取られます。
__id__は、数だけであるべきであり、Itemの数以上になってはいけないので、二つのチェックを書きました。これで、終わりです。作ってみましょう!
テスト
CMD / Terminal で、さっきのrunを閉じて、もう一回実行してください:
go run main.go
これで、localhost:8000/items
に行くと、このデータが見えます:
localhost:8000/items/0
やlocalhost:8000/items/1
も使えますが、間違えの__id__を入れたら、エラーが出て来ます。
PostmanでもJavascript clientでも、このRESTful APIをアクセスできます。
ソースコード
問題がある場合は、ソースコードを参照してください。
https://gist.github.com/sbenemerito/bf8cdc720a41e2d2ce2cc2fd760bbbd5
まとめ
今回は、簡単にするために、すごくシンプルなRESTful APIを作りましたが、これでgolangでRESTful APIを作成がどれほど簡単かを見えます。もちろん実際のプロジェクトでは、実勢の値を返すために、データベースにコネクトします。
このプロジェクトを続けることに興味があったら、次のステップはCRUDの機能をするとか、データベースにコネクトすることです。
以上