Introduction to the

Small programs can easily obtain the user identification provided by wechat through the login ability provided by wechat official, and quickly establish the user system within small programs.

A series of

  1. Cloud native API Gateway, GRPC-Gateway V2 Exploration

The business process

  • Official development access documentation

Initialize the project

The development environment

For less local development environment

go version
# go version go1.14.14 darwin/amd64
protoc --version
# libprotoc 3.15.7
protoc-gen-go --version
# protoc - gen - go v1.26.0
protoc-gen-go-grpc --version
# protoc - gen - go - GRPC 1.1.0
protoc-gen-grpc-gateway --version
Copy the code

Initial code structure

Initialize the go project with go mod init Server. Here (demo) I will use server as the current module name.

go-grpc-gateway-v2-microservice

Micro ├ ─ ─ auth / / authentication service │ ├ ─ ─ API │ ├ ─ ─ ├ ─ ─ gen │ ├ ─ ─ ├ ─ ─ ├ ─ ─ v1 / / the generated code will be put there, └ │ ├── ├.go, └ │ ├── ├.go, └ │ ├── ├.go, └ │ ├── ├.go, └ │ ├── ─ ├.go, ├── ── ── ── ── ── ── ── ─ .go //.go //.Go //.Go //.Go └─ └─ go ├─ gen.sh // Generate code according to 'auth. Proto'Copy the code

Domain (auth.proto) definition

syntax = "proto3"; package auth.v1; option go_package="server/auth/api/gen/v1; authpb"; // The client sends a code message LoginRequest {string code = 1; } // the developer server returns a custom login state (token) message LoginResponse {string access_token = 1; int32 expires_in = 2; } service AuthService {RPC Login (LoginRequest) returns (LoginResponse); }Copy the code

Expose RESTful JSON APIS using GRPC-gateway

auth.yamldefine

type: google.api.Service
config_version: 3

http:
  rules:
  - selector: auth.v1.AuthService.Login
    post: /v1/auth/login
    body: "*"
Copy the code

Generate code based on the configuration

usegen.shgenerategRPC-GatewayThe relevant code

PROTO_PATH=./auth/api
GO_OUT_PATH=./auth/api/gen/v1

protoc -I=$PROTO_PATH --go_out=paths=source_relative:$GO_OUT_PATH auth.proto
protoc -I=$PROTO_PATH --go-grpc_out=paths=source_relative:$GO_OUT_PATH auth.proto
protoc -I=$PROTO_PATH --grpc-gateway_out=paths=source_relative,grpc_api_configuration=$PROTO_PATH/auth.yaml:$GO_OUT_PATH auth.proto
Copy the code

Run:

sh gen.sh
Copy the code

Auth.pb. go, auth_grpc.pb.go, auth.pb.gw.go.

├ ─ ─ auth │ ├ ─ ─ API │ ├ ─ ─ ├ ─ ─ gen │ ├ ─ ─ ├ ─ ─ ├ ─ ─ v1 │ ├ ─ ─ ├ ─ ─ ├ ─ ─ ├ ─ ─ auth. Pb. Golang related protobuf code generated by the go / / │ ├ ─ ─ ├ ─ ─ ├ ─ ─ ├ ─ ─ auth_grpc. Pb. Go / / golang related gRPC Server code generation │ ├ ─ ─ ├ ─ ─ ├ ─ ─ ├ ─ ─ auth. Pb. Gw go / / golang related gRPC - Gateway code generation │ │ ├ ─ ─ auth. Proto │ │ └ ─ ─ auth. Yaml │ ├ ─ ─ auth │ │ └ ─ ─ auth. Go │ ├ ─ ─ wechat │ └ ─ ─ main. Go ├ ─ ─ gateway │ └ ─ ─ main. Go ├ ─ ─ gen. Sh └ ─ ─ go. The modCopy the code

Tidy up your bag:

go mod tidy
Copy the code

Preliminary implementation of Auth gRPC Service Server

implementationAuthServiceServerinterface

Let’s look at the auth_grpc.pb.go code and find the AuthServiceServer definition:

...// AuthServiceServer is the server API for AuthService service.
// All implementations must embed UnimplementedAuthServiceServer
// for forward compatibility
type AuthServiceServer interface{the Login (context. The context, * LoginRequest) (* LoginResponse, error) mustEmbedUnimplementedAuthServiceServer ()}...Copy the code

We implement it in auth/auth/auth.go:

Key code interpretation:

// Define the Service structure
type Service struct {
	Logger         *zap.Logger
	OpenIDResolver OpenIDResolver
	authpb.UnimplementedAuthServiceServer
}
// This is an abstraction for the user
// Define the interface to communicate with wechat third-party server
type OpenIDResolver interface {
	Resolve(code string) (string, error)
}
// Specific method implementation
func (s *Service) Login(c context.Context, req *authpb.LoginRequest) (*authpb.LoginResponse, error) {
	s.Logger.Info("received code",
		zap.String("code", req.Code))
	// Call the wechat server to get the user's unique id openId
	openID, err := s.OpenIDResolver.Resolve(req.Code)
	iferr ! =nil {
		return nil, status.Errorf(codes.Unavailable,
			"cannot resolve openid: %v", err)
	}
	// Debug the code like this
	return &authpb.LoginResponse{
		AccessToken: "token for open id " + openID,
		ExpiresIn:   7200,},nil
}
Copy the code

There is a very important programming concept that can be used well to get more results. Interface definitions are defined by the consumer, not the implementer, as in the Case of the OpenIDResolver interface.

implementationOpenIDResolverinterface

A third-party library of the community is used here, which is mainly used to complete the exchange from the developer server to the wechat server for the user’s unique identifier OpenID, the user’s unique identifier under the wechat open platform account UnionID (if the current small program has been bound to the wechat open platform account) and session key session_key. Of course, it’s easy to write your own without using the library.

go get -u github.com/medivhzhan/weapp/v2
Copy the code

We implement it in Auth /wechat/wechat. Go:

Key code interpretation:

// Repeat the same Service implementation
// AppID & AppSecret should be configurable and passed in from outside
type Service struct {
	AppID     string
	AppSecret string
}
func (s *Service) Resolve(code string) (string, error) {
	resp, err := weapp.Login(s.AppID, s.AppSecret, code)
	iferr ! =nil {
		return "", fmt.Errorf("weapp.Login: %v", err)
	}
	iferr = resp.GetResponseError(); err ! =nil {
		return "", fmt.Errorf("weapp response error: %v", err)
	}
	return resp.OpenID, nil
}
Copy the code

Configure the Auth Service gRPC Server

auth/main.go

func main(a) {
	logger, err := zap.NewDevelopment()
	iferr ! =nil {
		log.Fatalf("cannot create logger: %v", err)
	}
    // Configure the server listening port
	lis, err := net.Listen("tcp".": 8081")
	iferr ! =nil {
		logger.Fatal("cannot listen", zap.Error(err))
	}
    
    // Create a gRPC server
	s := grpc.NewServer()
	// Configure the specific Service
	authpb.RegisterAuthServiceServer(s, &auth.Service{
		OpenIDResolver: &wechat.Service{
			AppID:     "your-app-id",
			AppSecret: "your-app-secret",
		},
		Logger: logger,
	})
	// Start external service
	err = s.Serve(lis)
	iferr ! =nil {
	    logger.Fatal("cannot server", zap.Error(err))   
	}
}
Copy the code

Initial implementation of API Gateway

gateway/main.go

// Create a cancelable context (e.g.
c := context.Background()
c, cancel := context.WithCancel(c)
defer cancel()

mux := runtime.NewServeMux(runtime.WithMarshalerOption(
	runtime.MIMEWildcard,
	&runtime.JSONPb{
		MarshalOptions: protojson.MarshalOptions{
			UseEnumNumbers: true.// Enumeration field values use numbers
			UseProtoNames:  true.// The JSON key passed to clients is underlined '_'
			// AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
			// Access_token should be used
		},
		UnmarshalOptions: protojson.UnmarshalOptions{
			DiscardUnknown: true.// Ignore the nonexistent poroto field sent by client
		},
	},
))
err := authpb.RegisterAuthServiceHandlerFromEndpoint(
	c,
	mux,
	"localhost:8081",
	[]grpc.DialOption{grpc.WithInsecure()},
)
iferr ! =nil {
	log.Fatalf("cannot register auth service: %v", err)
}

err = http.ListenAndServe(": 8080", mux)
iferr ! =nil {
	log.Fatalf("cannot listen and server: %v", err)
}
Copy the code

test

// Send res.code to the backend for openId, sessionKey, unionId
wx.request({
  url: "http://localhost:8080/v1/auth/login".method: "POST".data: { code: res.code },
  success: console.log,
  fail: console.error,
})
Copy the code

Refs

  • Demo: go-grpc-gateway-v2-microservice
  • gRPC-Gateway
  • gRPC-Gateway Docs
I am weishao wechat: uuhells123 public number: hackers afternoon tea add my wechat (mutual learning exchange), pay attention to the public number (for more learning materials ~)Copy the code