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
- 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.yaml
define
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.sh
generategRPC-Gateway
The 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
implementationAuthServiceServer
interface
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.
implementationOpenIDResolver
interface
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