Go RESTful API 接口开发

news2024/9/23 3:32:23

文章目录

  • 什么是 RESTful API
  • Go 流行 Web 框架-Gin
  • Go HelloWorld
  • Gin 路由和控制器
  • Gin 处理请求参数
  • 生成 HTTP 请求响应
  • Gin 的学习内容
  • 实战用 Gin 框架开发 RESTful API
  • OAuth 2.0接口了解
  • 用 Go 开发 OAuth2.0 接口示例

编程有一个准则——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()
}

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;
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!"})
}

  • 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

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. 欢迎界面
<!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. Go 语言编写


package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"os"
)

// const clientID = "<your client id>"
const clientID = "5bcf804cfeb0ef7120f5"

// const clientSecret = "<your client secret>"
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"`
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1141510.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

强化学习------PPO算法

目录 简介一、PPO原理1、由On-policy 转化为Off-policy2、Importance Sampling&#xff08;重要性采样&#xff09;3、off-policy下的梯度公式推导 二、PPO算法两种形式1、PPO-Penalty2、PPO-Clip 三、PPO算法实战四、参考 简介 PPO 算法之所以被提出&#xff0c;根本原因在于…

简述一下伪共享的概念以及如何避免

缓存行 计算机工程师为了提高 CPU 的利用率&#xff0c;平衡 CPU 和内存之间的速度差异&#xff0c;在CPU 里面设计了三级缓存。 CPU 在向内存发起 IO 操作的时候&#xff0c;一次性会读取 64 个字节的数据作为一个缓存行&#xff0c;缓存到 CPU 的高速缓存里面。 在 Java 中一…

Codeforces Round 905 (Div. 3)ABCDEF

Codeforces Round 905 (Div. 3) 目录 A. Morning题意思路核心代码 B. Chemistry题意思路核心代码 C. Raspberries题意思路核心代码 D. In Love题意思路核心代码 E. Look Back题意思路核心代码 A. Morning 题意 从一开始&#xff0c;每一次操作可以选择当前的数字打印或者是移…

【代码随想录】算法训练计划03

1、203. 移除链表元素 题目&#xff1a; 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5] 思路&#xf…

【Unity实战】手戳一个自定义角色换装系统——2d3d通用

文章目录 每篇一句前言素材开始切换头型添加更改颜色随机控制头型和颜色新增眼睛同样的方法配置人物的其他部位设置相同颜色部位全部部位随机绘制UI并添加点击事件通过代码控制点击事件添加颜色修改的事件其他部位效果UI切换添加随机按钮保存角色变更数据跳转场景显示角色数据 …

Linux之J2EE的项目部署及发布

目录 前言 一、会议OA单体项目windows系统部署 1.检验工作 1. 检验jar项目包是否可以运行 2. 验证数据库脚本是否有误 3. 测试项目功能 2. 部署工作 2.1 传输文件 2.2 解压项目及将项目配置到服务器中 2.3 配置数据库 2.4 在服务器bin文件下点击startup.bat启动项目 …

Python----break关键字对while...else结构的影响

案例&#xff1a; 女朋友生气&#xff0c;要求道歉5遍&#xff1a;老婆大人&#xff0c;我错了。道歉到第三遍的时候&#xff0c;媳妇埋怨这一遍说的不真诚&#xff0c;是不是就是要退出循环了&#xff1f;这个退出有两种可能性&#xff1a; ① 更生气&#xff0c;不打算原谅…

最优秀的完整的数字音频工作站水果音乐FL Studio21.1.1.3750中文解锁版

FL Studio21.1.1.3750中文解锁版简称 FL 21&#xff0c;全称 Fruity Loops Studio 21&#xff0c;因此国人习惯叫它"水果"。目前最新版本是FL Studio21.1.1.3750中文解锁版版本&#xff0c;它让你的计算机就像是全功能的录音室&#xff0c;大混音盘&#xff0c;非常先…

连续离散混合系统控制与仿真

简单二阶系统 先研究最简单的二阶积分器串联型系统 x ˙ 1 x 2 x ˙ 2 u \begin{aligned} & \dot x_1 x_2 \\ & \dot x_2 u \\ \end{aligned} ​x˙1​x2​x˙2​u​ 使用零阶保持法离散化&#xff08;见附录&#xff09;&#xff0c; A [ 0 1 0 0 ] , B [ 0 …

Leetcode.1465 切割后面积最大的蛋糕

题目链接 Leetcode.1465 切割后面积最大的蛋糕 rating : 1445 题目描述 矩形蛋糕的高度为 h h h 且宽度为 w w w&#xff0c;给你两个整数数组 h o r i z o n t a l C u t s horizontalCuts horizontalCuts 和 v e r t i c a l C u t s verticalCuts verticalCuts&#xf…

Leetcode—66.加一【简单】

2023每日刷题&#xff08;十一&#xff09; Leetcode—66.加一 实现代码1 /*** Note: The returned array must be malloced, assume caller calls free().*/ int* plusOne(int* digits, int digitsSize, int* returnSize){int num 0;int i 0;int arr[110] {0};// 进位标识…

Linux MMC子系统 - 2.eMMC 5.1总线协议浅析

By: Ailson Jack Date: 2023.10.27 个人博客&#xff1a;http://www.only2fire.com/ 本文在我博客的地址是&#xff1a;http://www.only2fire.com/archives/161.html&#xff0c;排版更好&#xff0c;便于学习&#xff0c;也可以去我博客逛逛&#xff0c;兴许有你想要的内容呢。…

Java提升技术,进阶为高级开发和架构师的路线

原文网址&#xff1a;Java提升技术&#xff0c;进阶为高级开发和架构师的路线-CSDN博客 简介 Java怎样提升技术&#xff1f;怎样进阶为高级开发和架构师&#xff1f;本文介绍靠谱的成长路线。 首先点明&#xff0c;只写业务代码是无法成长技术的。提升技术的两个方法是&…

“从部署到优化,打造高效会议管理系统“

目录 引言一、部署单机项目 - 会议OA1.1 硬件和软件环境准备1.2 检查项目1.3 系统部署1.后端部署 二、部署前后端分离项目 - SPA项目后端部署2.前端部署 总结 引言 在现代化办公环境中&#xff0c;会议是组织沟通、决策和合作的重要方式之一。为了提高会议的效率和质量&#x…

MySQL-DQL【数据查询语言】(图码结合)

目录 一.DQL的定义 二.DQL—语法 三.DQL—基础查询(SELECT.. FROM) &#x1f449;查询多个字段 &#x1f449;设置别名 &#x1f449;去除重复记录 准备工作(建表&#xff0c;添加数据&#xff09; &DQL----基本查询的案例 五.DQL—条件查询(WHERE) 5.1 语法: 5…

基于 C# 实现样式与数据分离的打印方案

对于八月份的印象&#xff0c;我发现大部分都留给了出差。而九月初出差回来&#xff0c;我便立马投入了新项目的研发工作。因此&#xff0c;无论是中秋节还是国庆节&#xff0c;在这一连串忙碌的日子里&#xff0c;无不充满着仓促的气息。王北洛说&#xff0c;“活着不就是仓促…

数字化转型系列主题:数据中台知识体系

当前&#xff0c;大部分企业不再建设从源数据采集到分析应用的烟囱式系统&#xff0c;更倾向于数据集中采集、存储&#xff0c;并应用分层建设。这种方式一方面有利于应用系统的快速部署&#xff0c;另一方面也保证了数据的集中管理与运营&#xff0c;体现数据的资产、资源属性…

主动调度是如何发生的

计算机主要处理计算、网络、存储三个方面。计算主要是 CPU 和内存的合作&#xff1b;网络和存储则多是和外部设备的合作&#xff1b;在操作外部设备的时候&#xff0c;往往需要让出 CPU&#xff0c;就像上面两段代码一样&#xff0c;选择调用 schedule() 函数。 上下文切换主要…

初识《时间复杂度和空间复杂度》

目录 前言&#xff1a; 关于数据结构与算法 1.什么是数据结构&#xff1f; 2.什么是算法&#xff1f; 3.数据结构和算法的重要性 算法效率是什么&#xff1f; 1.怎样衡量一个算法的好坏呢&#xff1f; 2.算法的复杂度是个什么&#xff1f; 时间复杂度 1.时间复杂度的概…

Android Glide限定onlyRetrieveFromCache取内存缓存submit超时阻塞方式,Kotlin

Android Glide限定onlyRetrieveFromCache取内存缓存submit超时阻塞方式,Kotlin import android.os.Bundle import android.util.Log import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import com.b…