Protocol BuffersでAPI通信 サーバー編【golang】 

More than 1 year has passed since last update.


protocol buffersとは

API通信などで、Jsonのようなテキスト形式で通信を行わずに、protocol buffersはバイナリ形式で通信を行います。

proto file(事前にスキーマを定義するファイル)から、サーバー側とクライアント側に、シリアライズ・デシリアライズ用のファイルを書き出し、それらを利用してデータの送受信を行います。


Proto fileの定義

今回は都道府県のリストを返すAPIを作成して行きたいと思います。

まず、以下のようにproto fileを定義して行きいます。


prefectures.proto


syntax = "proto3";

option java_package = "com.takusemba.gouda.protos";
option java_outer_classname = "Prefectures";
option go_package = "proto";

package prefectures;

message Prefecture {
int64 id = 1; // ID
string name = 2; // 名前
string romaji = 3; // 読み方(ローマ字)
}

message GetPrefecturesResponse {
repeated Prefecture prefectures = 1;
}


実際のprotoのコードはこちらに上がっています。


protoからgoのファイルを書き出す

先ほど定義したproto fileから、server側のシリアライズ・デシリアライズ用のファイルを書き出します。

今回はgoのfileを書き出したいますが、どの言語でも書き出すことができます。

$ brew install protobuf

$ go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
$ protoc --proto_path=protos --go_out=proto protos/*.proto //<- fileの書き出し

実行するとこのようなgoファイルが書き出されているのがわかると思います。


APIを実装する

Prefectureのモデルを定義します。


models/prefecture.go



type Prefecture struct {
Id int `json:"id" bson:"_id"`
Name string `json:"name" bson:"name"`
Romaji string `json:"romaji" bson:"romaji"`
}

type Prefectures struct {
Prefectures []Prefecture `json:"prefectures"`
}


APIの実装はechoを使って実装したいと思います。


services/prefecture.go



package main

import (
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
proto1 "github.com/golang/protobuf/proto"
"net/http"
"github.com/TakuSemba/camembert/models"
"github.com/TakuSemba/camembert/proto"
)

func main() {
e := echo.New()
//middleware
e.Use(middleware.Logger())
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"*"},
AllowMethods: []string{echo.GET, echo.HEAD, echo.PUT, echo.PATCH, echo.POST, echo.DELETE},
}))

e.GET("/prefectures", func(c echo.Context) error {

// DBからPrefectureのリストと取ってくる
prefectures, _ := models.GetPrefectures()

res := ToGetPrefecturesResponse(prefectures)
data, _ := proto1.Marshal(&res)

return c.Blob(http.StatusOK, "application/protobuf", data)
})

e.Start(":80")
}

func ToGetPrefecturesResponse(prefectures []models.Prefecture) (res proto.GetPrefecturesResponse) {
prefecturesProto := ToPrefectures(prefectures)
res = proto.GetPrefecturesResponse{
Prefectures: prefecturesProto,
}
return
}

func ToPrefectures(prefectures []models.Prefecture) (res []*proto.Prefecture) {
for _, pref := range prefectures {
prefProto := ToPrefecture(&pref)
res = append(res, &prefProto)
}
return
}

func ToPrefecture(pref *models.Prefecture) (chProto proto.Prefecture) {
chProto = proto.Prefecture{
Id: int64(pref.Id),
Name: pref.Name,
Romaji: pref.Romaji,
}
return
}


以下でgo serverを起動するとhttp://your_url/prefecture で都道府県リストのバイナリを返すようになります。

$ go run main.go


テストコードを書く

実際にAPIを叩いて見て、意図した通りのバイナリが返却されているかをテストします。


prefecture_test.go



package services

import (
"io/ioutil"
"log"
"net/http"
"testing"

proto1 "github.com/golang/protobuf/proto"
"github.com/k0kubun/pp"
"github.com/TakuSemba/camembert/proto"
)

const (
reqUrl = "http://your_url"
)

func TestGetPrefectures(t *testing.T) {

url := reqUrl + "/prefectures"
log.Println("[TEST] ", url)

resp, _ := http.Get(url)
defer resp.Body.Close()

resData, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Log("error", err)
}

log.Println("Response:", resp.StatusCode)
if resp.StatusCode == 200 {
resProto := proto.GetPrefecturesResponse{}
if err = proto1.Unmarshal(resData, &resProto); err != nil {
log.Println("unmarshaling error :", err)
}
pp.Println(resProto)
} else {
resProto := proto.ErrorResult{}
if err = proto1.Unmarshal(resData, &resProto); err != nil {
log.Println("unmarshaling error :", err)
}
pp.Println(resProto)
}
}


以下のコマンドでテストを走らせることができます。

$ go test prefecture_test.go -v

最終的に、mongo + docker + golangで実装して、こちらのレポジトリ

で公開しているので、ぜひ見て見てくだい。