Help us understand the problem. What is going on with this article?

golangでシンプルなRESTful APIを作ってみよう!

More than 1 year has passed since last update.

Webからエンタープライズアプリケーションまで、RESTfulというアーキテクチャーが使われています。ステートレス設計として、別々のソフトウェアコンポーネント間の通信を提供する強力な方法であります。 Web開発者として、RESTful APIを作るために、主にPythonを使いますが、最近Goプログラミング言語に出会い、興味を持ってきました。Goプログラミング言語(golangでも呼ばれる)は、Googleのプロジェクトで、マイクロサービスアーキテクチャに適当な多くの機能も提供するので、人気が増えています。

本記事、golangを使ってシンプルなRESTful APIをどう作るか説明したいと思います。

前提条件

このチュートリアルがゼロからじゃなくて、前提として読者がこの三つのポイントが持っています:

仕様

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に行くと、このテキストが見えます:

image.png

homePage()の関数で、このテキストが見えます。でも、/itemsに行くと、何もが見えません。原因は、他の関数(特にreturnAllItems)がまだ何もしません。

サポート関数

これから、二つのパッケージが必要なので、インポートしましょう。最初のimport部分をこれに変更してください:

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strconv"

    "github.com/gorilla/mux"
)

encoding/jsonstrconvというパッケージを追加しました。もうすぐこの二つを使います。

それから、二つのサポート関数を書きましょう:

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をアクセスしたら、1idになります。それから、そのidは、indexとしてitemListという配列からデータを取ってJSONの形式で答えます。id1の場合は、二つ目のデータが取られます。そして、id0の場合は、一つ目のデータが取られます。

idは、数だけであるべきであり、Itemの数以上になってはいけないので、二つのチェックを書きました。これで、終わりです。作ってみましょう!

テスト

CMD / Terminal で、さっきのrunを閉じて、もう一回実行してください:

go run main.go

これで、localhost:8000/items に行くと、このデータが見えます:
image.png

localhost:8000/items/0localhost:8000/items/1も使えますが、間違えのidを入れたら、エラーが出て来ます。

image.png

PostmanでもJavascript clientでも、このRESTful APIをアクセスできます。

ソースコード

問題がある場合は、ソースコードを参照してください。
https://gist.github.com/sbenemerito/bf8cdc720a41e2d2ce2cc2fd760bbbd5

まとめ

今回は、簡単にするために、すごくシンプルなRESTful APIを作りましたが、これでgolangでRESTful APIを作成がどれほど簡単かを見えます。もちろん実際のプロジェクトでは、実勢の値を返すために、データベースにコネクトします。

このプロジェクトを続けることに興味があったら、次のステップはCRUDの機能をするとか、データベースにコネクトすることです。

以上

参考リソース

sbenemerito
日本語を勉強中の留学生。自らの技術力の向上に関心を持つ、ウェブ開発とシステムの自動化、変化を恐れないエンジニア。主に Python、Javascript 使い。
bit-okutama
外国人ITエンジニア育成を目的とした日本語学校です
http://bit-okutama.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away