Learn Go Documentation

website·Indexed 29 days ago

https://shibin-cli.github.io/learn-go/

A comprehensive guide to the Go programming language covering core concepts such as interfaces, coding specifications, and web development with Gin. It includes detailed sections on database management using GORM (CRUD, models, and relations) and remote procedure calls using RPC, Protocol Buffers, and gRPC, including interceptors and message validation.

Tokens
12.6K
Snippets
72
Records
76
Agent score
74%

What's inside Learn Go

  1. Understand Duck Typing in Go Interfaces

    Go uses a structural type system similar to 'duck typing' ('if it walks like a duck and quacks like a duck, it's a duck'). Interfaces in Go describe the external behavior (methods) of an object rather than its internal structure. A type implements an interface implicitly by implementing all the methods defined in that interface, without needing an explicit 'implements' keyword.
  2. Delete records and Soft Delete mechanism

    Use Delete to remove records. If the model includes a gorm.DeletedAt field, GORM performs a Soft Delete, marking the record as deleted by setting the timestamp instead of removing the row. Soft-deleted records are automatically excluded from normal queries.

    • To query soft-deleted records, use Unscoped().
    • To permanently delete a record (Hard Delete), use Unscoped().Delete().
    // Delete by primary key
    db.Delete(&user)
    db.Delete(&User{}, 10)
    
    // Batch delete with condition
    db.Where("email LIKE ?", "%shibin%").Delete(&Email{})
    
    // Query including soft-deleted records
    db.Unscoped().Find(&users)
    
    // Permanent (Hard) delete
    db.Unscoped().Delete(&user)
  3. Implement gRPC streaming patterns in Go

    gRPC supports three types of streaming patterns in addition to simple unary RPCs: Server-side streaming, Client-side streaming, and Bidirectional streaming. These are defined in the .proto file using the stream keyword in the return type or the request parameter.
    syntax = "proto3";
    
    option go_package = "./;stream";
    
    service Stream {
      // Server-side streaming: Client sends one request, server sends a stream of responses
      rpc GetStream(StreamReqData) returns (stream StreamResData);
      
      // Client-side streaming: Client sends a stream of requests, server sends one response
      rpc PutStream(stream StreamReqData) returns (StreamResData);
      
      // Bidirectional streaming: Both client and server send a stream of messages
      rpc AllStream(stream StreamReqData) returns (stream StreamResData);
    }
    
    message StreamReqData {
      string data = 1;
    }
    message StreamResData {
      string data = 1;
    }
  4. Write Go comments and documentation

    Maintain a consistent commenting style for better maintainability:

    • General Style: Use Chinese or English. Use spaces to separate Chinese and English characters, as well as between English and Chinese punctuation. Keep single-line comments under 120 characters.
    • Function Comments: Use block comments to describe the function, parameters, and return values.
    • Structs/Interfaces: Every custom struct or interface must have a brief description on the line immediately preceding the definition in the format: StructName, Description.
    • Package Comments: Include a basic introduction, creator's name, and creation date (yyyyMMdd).
    /*
    Two numbers addition
    
    Parameters:
        a  Addend
        b Augend
    
    Return:
        The result of addition
    */
    func add(a, b int) int {
        return a + b
    }
  5. Implement middleware in Gin

    Middleware can be applied globally using r.Use() or to specific route groups. To stop the execution of subsequent handlers in the chain, call c.Abort(). Use c.Next() to yield control to the next handler.
    func authRequired() gin.HandlerFunc {
    	return func(c *gin.Context) {
    		token := c.GetHeader("X-Token")
    		if token != "shibin" {
    			c.JSON(http.StatusUnauthorized, gin.H{"msg": "invalid token"})
    			c.Abort() // Prevents subsequent handlers from executing
    			return
    		}
    		c.Next()
    	}
    }
    
    func main() {
    	r := gin.New()
    	r.Use(authRequired())
    	r.Run(":8080")
    }
  6. Expose Go RPC services over HTTP

    To transport RPC calls over HTTP instead of raw TCP, use http.HandleFunc to wrap the request body and response writer into an io.ReadWriteCloser, then pass it to rpc.ServeRequest using the jsonrpc codec.
    // Server side
    http.HandleFunc("/json", func(w http.ResponseWriter, r *http.Request) {
        var conn io.ReadWriteCloser = struct {
            io.Writer
            io.ReadCloser
        }{
            ReadCloser: r.Body,
            Writer:     w,
        }
        rpc.ServeRequest(jsonrpc.NewServerCodec(conn))
    })
    http.ListenAndServe(":8080", nil)
    
    // Client side (Python example using requests)
    import requests
    request = {"id": 0, "params": ["body"], "method": "HelloService.Hello"}
    res = requests.post("http://localhost:8080/json", json=request)
    print(res.text)
  7. Bind and validate request data using structs

    Use model binding to map request data to Go structs. Gin uses go-playground/validator for validation via struct tags.

    • Use json:"fieldname" for JSON binding.
    • Use binding:"required" to make a field mandatory.
    • Use binding:"min=x,max=y,email,len=x" for specific validation rules.

    Binding Methods:

    • Must Bind: Bind, BindJSON, BindXML, BindQuery, BindYAML (automatically sets 400 error on failure).
    • Should Bind: ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML (returns error for manual handling).

    Specific Binding Types:

    • ShouldBindUri: For URL path parameters (uses uri tag).
    • ShouldBindQuery: For query string parameters (uses form tag).
    type Signup struct {
    	Username string `json:"username" binding:"required,min=3,max=30"`
    	Email    string `json:"email" binding:"required,email"`
    }
    
    r.POST("/login", func(c *gin.Context) {
      var user Signup
      if err := c.ShouldBind(&user); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
        return
      }
      c.JSON(http.StatusOK, gin.H{"user": user})
    })
  8. Define and implement an interface in Go

    To create an interface in Go, use the type keyword followed by the interface name and the interface keyword. The body contains the method signatures that any implementing type must provide. A concrete type (like a struct) implements the interface by defining methods with matching signatures.
    package main
    
    import "fmt"
    
    // Define the interface
    type Duck interface {
    	Gaga()
    	Swim()
    }
    
    // Define a concrete type
    type ToyDuck struct {}
    
    // Implement the Gaga method for ToyDuck
    func (td *ToyDuck) Gaga() {
    	fmt.Println("嘎嘎...")
    }
    
    // Implement the Swim method for ToyDuck
    func (td *ToyDuck) Swim() {
    	fmt.Println("游泳..")
    }
    
    func main() {
    	// Assign a pointer to ToyDuck to the Duck interface
    	var duck Duck = &ToyDuck{}
    	duck.Gaga()
    	duck.Swim()
    }
  9. Install gRPC and Protocol Buffers for Go

    To use gRPC in Go, you need the Protocol Buffer compiler (protoc) and the Go-specific plugins for generating code.
    # Install protoc (Mac)
    brew install protoc
    
    # Install Go plugins for protobuf and gRPC
    go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
    go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
    
    # Add Go bin directory to PATH
    export PATH="$PATH:$(go env GOPATH)/bin"
  10. Implement a gRPC Unary Server Interceptor

    Server interceptors can be used for cross-cutting concerns like authentication. You can pass a unary interceptor to grpc.NewServer using the grpc.UnaryInterceptor option. The interceptor function can inspect the context (e.g., for tokens in metadata) and decide whether to proceed with the handler or return an error.
    func interceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
        md, ok := metadata.FromIncomingContext(ctx)
        if !ok {
            return resp, status.Error(codes.Unauthenticated, "no token authentication info")
        }
        var token string
        if val, ok := md["token"]; ok {
            token = val[0]
        }
        fmt.Println("token", token)
        res, err := handler(ctx, req)
        return res, err
    }
    
    func main() {
        s := grpc.NewServer(grpc.UnaryInterceptor(interceptor))
        hello.RegisterHelloServiceServer(s, &Server{})
        serve, _ := net.Listen("tcp", "0.0.0.0:8080")
        _ = s.Serve(serve)
    }