• Go RESTful API 接口开发


    编程有一个准则——Don‘t Repeat Yourself(不要重复你的代码)。这个准则的核心概念是:如果有一些出现重复的代码,则应该把这些代码提取出来封装成一个方法。
    随着时间的积累,有了一批方法,可以把它们整合成工具类。如果工具类形成了规模,则可以把它们整合成类库。类库更系统,功能更全。不仅不要自己重复造项目中已有的“轮子”,也不要造别人已经造好的“轮子”,直接使用已有的“轮子”即可。

    什么是 RESTful API

    • 资源概述
      • 资源可以是单例或集合
      • 资源也可以包含子集合资源
      • REST API 使用统一资源标识符(URI)来定位资源
    • 使用名词表示资源
      • 文档
      • 集合
      • 存储
      • 控制器
    • 保持一致性
      • 使用正斜杠( \ )表示层次关系
      • 不要在 URI 尾部使用正斜杠
      • 使用连字符( - )来提高 URI 的可读性
      • 不要使用下划线( _ )
      • 在 URI 中使用小写字母
      • 不要使用文件拓展名
    • 切勿在 URI 中使用 CRUD 函数的名称
    • 使用查询参数过滤 URI 集合

    Go 流行 Web 框架-Gin

    Go HelloWorld

    package main
    
    import (
    	"github.com/gin-gonic/gin"
    )
    
    func main() {
    	// 创建一个默认的路由引擎
    	r := gin.Default()
    	// GET:请求方式;/hello:请求的路径
    	// 当客户端以GET方法请求/hello路径时,会执行后面的匿名函数
    	r.GET("/hello", func(c *gin.Context) {
    		// c.JSON:返回JSON格式的数据
    		c.JSON(200, gin.H{
    			"message": "Hello world!",
    		})
    	})
    	// 启动HTTP服务,默认在0.0.0.0:8080启动服务
    	r.Run()
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    Gin 路由和控制器

    • 路由规则
      • HTTP 请求方法
        • GET
        • POST
        • PUT
        • DELETE
      • URL 路径
        • 静态 URL 路径
        • 带路径的 URL 参数
        • 带星号(*)模糊匹配参数的 URL 路径
      • 处理器函数
    • 分组路由

    Gin 处理请求参数

    • 获取 GET 请求参数
    • 获取 POST 请求参数
    • 获取 URL 路径参数
    • 将请求参数绑定到结构体

    生成 HTTP 请求响应

    • 以字符串方式生成 HTTP 请求响应
    • 以 JSON 格式生成 HTTP 请求响应
    • 以 XML 格式生成 HTTP 请求响应
    • 以文件格式生成 HTTP 请求响应
    • 设置 HTTP 响应头

    Gin 的学习内容

    • Gin 渲染 HTML 模板
    • Gin 处理静态资源
    • Gin 处理 cookie
    • Gin 文件上传
    • Gin 中间件
    • Gin Session

    实战用 Gin 框架开发 RESTful API

    mysql> CREATE TABLE `users` (
        ->     `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
        ->     `phone` VARCHAR(255) DEFAULT NULL,
        ->     `name` VARCHAR(255) DEFAULT NULL,
        ->     `password` VARCHAR(255) DEFAULT NULL,
        ->     PRIMARY KEY (`id`)
        -> ) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    package main
    
    import (
    	"crypto/sha256"
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"gorm.io/driver/mysql"
    	"gorm.io/gorm"
    	"net/http"
    )
    
    type (
    	User struct {
    		ID       uint   `json:"id" gorm:"column:id"`
    		Phone    string `json:"phone" gorm:"column:phone"`
    		Name     string `json:"name" gorm:"column:name"`
    		Password string `json:"password" gorm:"column:password"`
    	}
    
    	UserRes struct {
    		ID    uint   `json:"id"`
    		Phone string `json:"phone"`
    		Name  string `json:"name"`
    	}
    )
    
    var db *gorm.DB
    
    func main() {
    	// Connect to the database
    	var err error
    	dsn := "root:mm..1213@tcp(127.0.0.1:3306)/UserManager?charset=utf8mb4&parseTime=True&loc=Local"
    	db, err = gorm.Open(mysql.New(mysql.Config{
    		DriverName: "mysql",
    		DSN:        dsn,
    	}), &gorm.Config{})
    	if err != nil {
    		panic("Failed to connect to database")
    	}
    
    	// Auto migrate the User struct to create the corresponding table in the database
    	err = db.AutoMigrate(&User{})
    	if err != nil {
    		panic("Failed to migrate the database")
    	}
    
    	router := gin.Default()
    	v2 := router.Group("/api/v2/user")
    	{
    		v2.POST("/", createUser)
    		v2.GET("/", fetchAllUser)
    		v2.GET("/:id", fetchUser)
    		v2.PUT("/:id", updateUser)
    		v2.DELETE("/:id", deleteUser)
    	}
    	router.Run(":8080")
    }
    
    func createUser(c *gin.Context) {
    	phone := c.PostForm("phone")
    	name := c.PostForm("name")
    	user := User{
    		Phone:    phone,
    		Name:     name,
    		Password: md5Password(phone),
    	}
    
    	tx := db.Begin()
    	if err := tx.Create(&user).Error; err != nil {
    		tx.Rollback()
    		c.JSON(http.StatusInternalServerError, gin.H{
    			"error": err.Error(),
    		})
    		return
    	}
    	tx.Commit()
    
    	c.JSON(http.StatusCreated, gin.H{
    		"status":  http.StatusCreated,
    		"message": "User created successfully!",
    		"ID":      user.ID,
    	})
    }
    
    func md5Password(password string) string {
    	hash := sha256.Sum256([]byte(password))
    	return fmt.Sprintf("%x", hash)
    }
    
    func fetchAllUser(c *gin.Context) {
    	var user []User
    	var _userRes []UserRes
    	db.Find(&user)
    	if len(user) <= 0 {
    		c.JSON(
    			http.StatusNotFound,
    			gin.H{
    				"status":  http.StatusNotFound,
    				"message": "No user found!",
    			})
    		return
    	}
    	for _, item := range user {
    		_userRes = append(_userRes,
    			UserRes{
    				ID:    item.ID,
    				Phone: item.Phone,
    				Name:  item.Name,
    			})
    	}
    	c.JSON(http.StatusOK,
    		gin.H{
    			"status": http.StatusOK,
    			"data":   _userRes,
    		})
    }
    
    func fetchUser(c *gin.Context) {
    	var user User
    	ID := c.Param("id")
    	db.First(&user, ID)
    	if user.ID == 0 {
    		c.JSON(http.StatusNotFound, gin.H{"status": http.StatusNotFound, "message": "No user found!"})
    		return
    	}
    	res := UserRes{
    		ID:    user.ID,
    		Phone: user.Phone,
    		Name:  user.Name,
    	}
    	c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": res})
    }
    
    func updateUser(c *gin.Context) {
    	var user User
    	userID := c.Param("id")
    	db.First(&user, userID)
    	if user.ID == 0 {
    		c.JSON(http.StatusNotFound, gin.H{"status": http.StatusNotFound, "message": "No user found!"})
    		return
    	}
    	db.Model(&user).Update("phone", c.PostForm("phone"))
    	db.Model(&user).Update("name", c.PostForm("name"))
    	c.JSON(http.StatusOK, gin.H{
    		"status":  http.StatusOK,
    		"message": "Updated User Successfully!",
    	})
    }
    
    func deleteUser(c *gin.Context) {
    	var user User
    	userID := c.Param("id")
    	db.First(&user, userID)
    	if user.ID == 0 {
    		c.JSON(http.StatusNotFound, gin.H{
    			"status":  http.StatusNotFound,
    			"message": "No user found!",
    		})
    		return
    	}
    	db.Delete(&user)
    	c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "message": "User deleted successfully!"})
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • GoLand Tools-Http Client 测试
    DELETE http://127.0.0.1:8080/api/v2/user/58
    Content-Type: application/x-www-form-urlencoded
    
    phone=10086&name=chYiDong
    
    • 1
    • 2
    • 3
    • 4

    OAuth 2.0接口了解

    用 Go 开发 OAuth2.0 接口示例

    • 做了解之后用到可能性比较小
    1. GitHub OAuth 应用注册
    • 注册页面:https://github.com/settings/applications/new
      在这里插入图片描述
    1. 登录授权页面
    <!DOCTYPE HTML>
    <html>
    <body>
    <a href="https://github.com/login/oauth/authorize?client_id=5bcf804cfeb0ef7120f5&redirect_uri=http://localhost:8087/oauth/redirect">
        Login by GitHub
    </a>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 欢迎界面
    <!DOCTYPE HTML>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, INItial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Hello</title>
    </head>
    <body>
    </body>
    <script>
        //获取url参数
        function getQueryVariable(variable) {
            var query = window.location.search.substring(1);
            var vars = query.split("&");
            for (var i = 0; i < vars.length; i++) {
                var pair = vars[i].split("=");
                if (pair[0] == variable) {
                    return pair[1];
                }
            }
            return (false);
        }
        // 获取access_token
        const token = getQueryVariable("access_token");
        // 调用用户信息接口
        fetch('https://api.github.com/user', {
            headers: {
                Authorization: 'token ' + token
            }
        })
        // 解析请求的JSON
         .then(res => res.json())
         .then(res => {
            // 返回用户信息
            const nameNode = document.createTextNode(`Hi, ${res.name}, Welcome to login our site by GitHub!`)
            document.body.appendChild(nameNode)
         })
    </script>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    1. Go 语言编写
    
    
    package main
    
    import (
    	"encoding/json"
    	"fmt"
    	"html/template"
    	"net/http"
    	"os"
    )
    
    // const clientID = ""
    const clientID = "5bcf804cfeb0ef7120f5"
    
    // const clientSecret = ""
    const clientSecret = "8d31102da18096d13eb6ec819cd81ca898ed7189"
    
    func hello(w http.ResponseWriter, r *http.Request) {
    	if r.Method == "GET" {
    		t, _ := template.ParseFiles("hello.html")
    		t.Execute(w, nil)
    	}
    }
    
    func login(w http.ResponseWriter, r *http.Request) {
    	if r.Method == "GET" {
    		t, _ := template.ParseFiles("login.html")
    		t.Execute(w, nil)
    	}
    }
    
    func main() {
    	http.HandleFunc("/login", login)
    	http.HandleFunc("/", hello)
    	http.HandleFunc("/hello", hello)
    
    	httpClient := http.Client{}
    	http.HandleFunc("/oauth/redirect", func(w http.ResponseWriter, r *http.Request) {
    		err := r.ParseForm()
    		if err != nil {
    			fmt.Fprintf(os.Stdout, "could not parse query: %v", err)
    			w.WriteHeader(http.StatusBadRequest)
    		}
    		code := r.FormValue("code")
    
    		reqURL := fmt.Sprintf("https://github.com/login/oauth/access_token?"+
    			"client_id=%s&client_secret=%s&code=%s", clientID, clientSecret, code)
    		req, err := http.NewRequest(http.MethodPost, reqURL, nil)
    		if err != nil {
    			fmt.Fprintf(os.Stdout, "could not create HTTP request: %v", err)
    			w.WriteHeader(http.StatusBadRequest)
    		}
    		req.Header.Set("accept", "application/json")
    
    		res, err := httpClient.Do(req)
    		if err != nil {
    			fmt.Fprintf(os.Stdout, "could not send HTTP request: %v", err)
    			w.WriteHeader(http.StatusInternalServerError)
    		}
    		defer res.Body.Close()
    
    		var t AccessTokenResponse
    		if err := json.NewDecoder(res.Body).Decode(&t); err != nil {
    			fmt.Fprintf(os.Stdout, "could not parse JSON response: %v", err)
    			w.WriteHeader(http.StatusBadRequest)
    		}
    
    		w.Header().Set("Location", "/hello.html?access_token="+t.AccessToken)
    		w.WriteHeader(http.StatusFound)
    	})
    
    	http.ListenAndServe(":8087", nil)
    }
    
    type AccessTokenResponse struct {
    	AccessToken string `json:"access_token"`
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
  • 相关阅读:
    Metabase学习教程:视图-7
    艾美捷衣霉素Tunicamycin 体内外研究&文献参考
    内存优化:Boxing
    Rust 最常用函数
    Nuxt.js头部魔法:轻松自定义页面元信息,提升用户体验
    C++类模板实例化与专门化
    Java泛型理解
    神经网络中间层输出可视化
    ios 开发问题小集 [持续更新]
    Python爬虫实战案例——第五例
  • 原文地址:https://blog.csdn.net/qq_61735602/article/details/134076278