42
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Golang grpc-gatewayでRESTful APIを作成する

Posted at

#はじめに
「gRPCを使ってみたものの、REST-APIは使えないのか?」
「gRPCに加えて、REST-APIを使いたい」
「gRPCで作ったものを簡単に検証したい」

と思って調べると、grpc-gatewayというものがありましたので、使ってみます。
https://github.com/grpc-ecosystem/grpc-gateway

#gRPC
gRPCについては、別途記事を書いていますので、こちらを参照してください。
https://qiita.com/ryu3/items/d5cc28a412a06e17ef98

#grpc-gatewayの概要

grpc-gatewayはgRPCで書かれたAPIを、JSON over HTTPのAPIに変換してくれます。コード生成器として機能し、ある種のリバースプロキシサーバーを生成します。下の図をご覧ください。

image.png

#インストール

下記コマンドでインストールしてください。

$ go get -u -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
$ go get -u -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
$ go get -u -v github.com/golang/protobuf/protoc-gen-go

#使ってみよう
##1.gRPCサービスを定義

proto/service.proto
syntax = "proto3";
package example;

import "google/api/annotations.proto";

service HelloWorldService {
  rpc SayHello (HelloRequest) returns (HelloReply) {
  }

  rpc GetUser(GetUserRequest) returns (User) {
  }

  rpc CreateUser(CreateUserRequest) returns (User) {
  }
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

message GetUserRequest {
  string id = 1;
}

message CreateUserRequest {
  string name = 1;
}

message User {
  string id = 1;
  string name = 2;
}

##2.google.api.httpを追加します。

service.proto
syntax = "proto3";
package example;

import "google/api/annotations.proto";

service HelloWorldService {
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      get: "/v1/example/sayhello/{name}"
    };
  }

  rpc GetUser(GetUserRequest) returns (User) {
    option (google.api.http) = {
        get: "/v1/example/users/{id}"
    };
  }

  rpc CreateUser(CreateUserRequest) returns (User) {
      option (google.api.http) = {
          post: "/v1/example/users"
          body: "*"
      };
  }
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

message GetUserRequest {
  string id = 1;
}

message CreateUserRequest {
  string name = 1;
}

message User {
  string id = 1;
  string name = 2;
}

##3.gRPC stub を生成する

protocコンパイラを使って、.protoファイルをビルドして、.pb.goファイルを作成します

protoc -I/usr/local/include -I. \
  -I$GOPATH/src \
  -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
  --go_out=plugins=grpc:. \
  service.proto

path/to/your_service.pb.goが生成されます。

##4.reverse-proxyを生成する

protocコンパイラを使って、.protoファイルをビルドして、.pb.gw.goファイルを作成します

protoc -I/usr/local/include -I. \
  -I$GOPATH/src \
  -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
  --grpc-gateway_out=logtostderr=true:. \
  service.proto

##5. Gateway / サーバの作成

gateway / サーバのを下記のように作成します。

greeter_server/main.go
package main

import (
	"context"
	"log"
	"net"

	"google.golang.org/grpc"
	pb "../proto"
)

const (
	port = ":5001"
)

// server is used to implement HelloWorldServer.
type server struct{}

// SayHello implements HelloWorldServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	log.Printf("Received: %v", in.Name)
	return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

// GetUser
func (s *server) GetUser(ctx context.Context, in *pb.GetUserRequest) (*pb.User, error) {
	log.Printf("Received: %v", in.Id)
	return &pb.User{
		Id: in.Id,
		Name: "SampleUser"}, nil
}

// CreateUser
func (s *server) CreateUser(ctx context.Context, in *pb.CreateUserRequest) (*pb.User, error) {
	log.Printf("Received: %v", in.Name)
	return &pb.User{
		Id: "123",
		Name: in.Name}, nil
}

func main() {
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterHelloWorldServiceServer(s, &server{})
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

greeter_gateway/main.go
package main

import (
  "flag"
  "fmt"
  "net/http"

  "github.com/golang/glog"
  "golang.org/x/net/context"
  "github.com/grpc-ecosystem/grpc-gateway/runtime"
  "google.golang.org/grpc"

  gw "../proto"
)

func run() error {
  ctx := context.Background()
  ctx, cancel := context.WithCancel(ctx)
  defer cancel()

  mux := runtime.NewServeMux()
  opts := []grpc.DialOption{grpc.WithInsecure()}
  endpoint := fmt.Sprintf("localhost:5001")
  err := gw.RegisterHelloWorldServiceHandlerFromEndpoint(ctx, mux, endpoint, opts)
  if err != nil {
    return err
  }

  return http.ListenAndServe(":5000", mux)
}

func main() {
  flag.Parse()
  defer glog.Flush()

  if err := run(); err != nil {
    glog.Fatal(err)
  }
}

##動作確認

gatewayとサーバを実行してみましょう。

go run greeter_gateway/main.go

別のターミナルを開いて、下記を実行します。

go run greeter_server/main.go

curlコマンドでREST APIを実行してみましょう。
期待通りにデータを取得できています。

ユーザ名(nakata)を入れて、sayhelloへGETします。

curl -X GET http://localhost:5000/v1/example/sayhello/nakata

{"message":"Hello nakata"}

ユーザID(10)を入れて、usersへGETします。

curl -X GET http://localhost:5000/v1/example/users/10

{"id":"10","name":"SampleUser"}

ユーザ名(nakata)を入れて、usersへPOSTします。

curl -X POST http://localhost:5000/v1/example/users -d '{"name":"nakata"}'

{"id":"123","name":"nakata"}

#おわりに
gRPCに対応するRESTful APIを自動生成しました。
gRPCサーバの実装をcurlコマンドで確認することができました。

42
34
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
42
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?