使用 Go 构建一个最小的 API 应用
最近有项目要使用 Go 开发,作为一个. NET Core 选手,准备先撸一个包含 CRUD 的最小 MVP 项目练手。
要创建一个 TODO 应用,会创建下面这些接口:
| API | Description | Request body | Response body |
|---|---|---|---|
GET /todoitems | Get all to-do items | None | Array of to-do items |
GET /todoitems/complete | Get completed to-do items | None | Array of to-do items |
GET /todoitems/{id} | Get an item by ID | None | To-do item |
POST /todoitems | Add a new item | To-do item | To-do item |
PUT /todoitems/{id} | Update an existing item | To-do item | None |
DELETE /todoitems/{id} | Delete an item | None | None |
我觉得,做这样一个 API 应用,不管是 Go 还是其他语言,思路是一样的,无外乎:SDK 版本、开发工具、服务容器、HTTP 请求和响应处理、数据库对应的语言驱动、实体定义和映射、JSON 处理等等。因此,其他语言怎么做,换成 Go 之后,找对应的工具和实现方案就可以了。
1 、快速搭建开发环境
- 官方下载 SDK:Download and install - The Go Programming Language
- 安装 VS Code:Download Visual Studio Code - Mac, Linux, Windows
- 安装扩展:“Go”
- 安装 Go 工具包:
ctrl+shift+p,输入go install回车后,选择全部工具安装
2、构建 API
2 .1、创建目录,初始化项目
go mod init todo-list-api
安装依赖包:
- gorilla/mux 是一个强大的 HTTP 路由器和 URL 匹配器,用于构建 Go Web 服务器: gorilla/mux: Package gorilla/mux is a powerful HTTP router and URL matcher for building Go web servers with 🦍 (github.com)
go get -u github.com/gorilla/mux
- Postgres 数据库的 Go 驱动包:lib/pq: Pure Go Postgres driver for database/sql (github.com)
go get github.com/lib/pq
2.2、入口函数
新增 main.go 文件,内容如下:
func main() {r := mux.NewRouter()r.HandleFunc("/api/todoitems", GetToDoItems).Methods("GET")r.HandleFunc("/api/todoitems/complete", GetToDoItemInCompleted).Methods("GET")r.HandleFunc("/api/todoitems/{id}", GetToDoItemById).Methods("GET")r.HandleFunc("/api/todoitems", CreateToDoItem).Methods("POST")r.HandleFunc("/api/todoitems/{id}", DeleteToDoItem).Methods("DELETE")r.HandleFunc("/api/todoitems/{id}", UpdateToDoItem).Methods("PUT")srv := &http.Server{Handler: r,Addr: ":3000",WriteTimeout: 15 * time.Second,ReadTimeout: 15 * time.Second,}log.Fatal(srv.ListenAndServe())
}
HandleFunc 函数第一个参数是路由路径,第二个参数是处理函数,然后链式调用 Methods 指定可以处理的 HTTP 请求类型。
2.3、实体和数据库
新增 db.go 文件,内容如下:
var DB *sql.DB
func ConnectToDatabase() {connStr := "user=postgres dbname=postgres password=Mysoft7789 sslmode=disable"db, err := sql.Open("postgres", connStr)if err != nil {log.Fatal(err)}DB = db
}
这个函数用来打开并获取数据库连接,放在 init 方法中调用
func init() {ConnectToDatabase()
}
定义结构类型 ToDo,内容如下:
type Todo struct {Id int `json:"id"`Name string `json:"name"`IsComplete bool `json:"is_complete"`
}
创建 postgres 数据表
create table public.t_todo_item
( id integer generated by default as identity, name varchar(20), is_complete boolean
);
2 .4、实现 API
新增 todo-service.go 实现所有的 API
func GetToDoItems(w http.ResponseWriter, r *http.Request) {rows, err := DB.Query("select id,name,is_complete from t_todo_item")if err != nil {panic(err.Error())}var todos []Todofor rows.Next() {var todo Todoerr = rows.Scan(&todo.Id, &todo.Name, &todo.IsComplete)if err != nil {log.Printf("Error happened in scan row. Err: %s", err)w.WriteHeader(http.StatusInternalServerError)return}todos = append(todos, todo)}jsonResp, err := json.Marshal(todos)if err != nil {log.Fatalf("Error happened in JSON marshal. Err: %s", err)w.WriteHeader(http.StatusInternalServerError)return}w.Header().Set("Content-Type", "application/json")w.Write(jsonResp)
}func GetToDoItemInCompleted(w http.ResponseWriter, r *http.Request) {rows, err := DB.Query("select id,name,is_complete from t_todo_item where is_complete = true")if err != nil {panic(err.Error())}var todos []Todofor rows.Next() {var todo Todoerr = rows.Scan(&todo.Id, &todo.Name, &todo.IsComplete)if err != nil {log.Printf("Error happened in scan row. Err: %s", err)w.WriteHeader(http.StatusInternalServerError)return}todos = append(todos, todo)}jsonResp, err := json.Marshal(todos)if err != nil {log.Fatalf("Error happened in JSON marshal. Err: %s", err)w.WriteHeader(http.StatusInternalServerError)return}w.Header().Set("Content-Type", "application/json")w.Write(jsonResp)
}func CreateToDoItem(w http.ResponseWriter, r *http.Request) {// convert todo struct from bodyvar todo Todoerr := json.NewDecoder(r.Body).Decode(&todo)if err != nil {log.Printf("Error happened in JSON unmarshal. Err: %s", err)return}// insert into dberr = DB.QueryRow("insert into t_todo_item (name, is_complete) values ($1, $2) RETURNING id", todo.Name, false).Scan(&todo.Id)if err != nil {log.Printf("Error happened in insert into db. Err: %s", err)w.WriteHeader(http.StatusInternalServerError)return}// return created todojsonResp, _ := json.Marshal(todo)w.Header().Set("Content-Type", "application/json")w.Write(jsonResp)
}func GetToDoItemById(w http.ResponseWriter, r *http.Request) {vars := mux.Vars(r)id := vars["id"]row := DB.QueryRow("select id,name, is_complete from t_todo_item where id = $1", id)var todo Todorow.Scan(&todo.Id, &todo.Name, &todo.IsComplete)jsonResp, err := json.Marshal(todo)if err != nil {log.Fatalf("Error happened in JSON marshal. Err: %s", err)w.WriteHeader(http.StatusInternalServerError)return}w.Header().Set("Content-Type", "application/json")w.Write(jsonResp)
}func DeleteToDoItem(w http.ResponseWriter, r *http.Request) {// check id from urlvars := mux.Vars(r)// convert id to intid, err := strconv.Atoi(vars["id"])if err != nil {log.Printf("Error happened in convert id to int. Err: %s", err)w.WriteHeader(http.StatusBadRequest)return}// if id less then 0, return 400if condition := id < 0; condition {w.WriteHeader(http.StatusBadRequest)return}// delete from db_, err = DB.Exec("delete from t_todo_item where id = $1", id)if err != nil {log.Printf("Error happened in delete from db. Err: %s", err)w.WriteHeader(http.StatusInternalServerError)return}w.WriteHeader(http.StatusOK)
}func UpdateToDoItem(w http.ResponseWriter, r *http.Request) {// check id from urlvars := mux.Vars(r)// convert id to intid, err := strconv.Atoi(vars["id"])if err != nil {log.Printf("Error happened in convert id to int. Err: %s", err)w.WriteHeader(http.StatusBadRequest)return}// if id less then 0, return 400if condition := id < 0; condition {w.WriteHeader(http.StatusBadRequest)return}// convert todo struct from bodyvar todo Todoerr = json.NewDecoder(r.Body).Decode(&todo)if err != nil {log.Printf("Error happened in JSON unmarshal. Err: %s", err)}todo.Id = id// update db_, err = DB.Exec("update t_todo_item set name = $1, is_complete = $2 where id = $3", todo.Name, todo.IsComplete, id)if err != nil {log.Printf("Error happened in update db. Err: %s", err)w.WriteHeader(http.StatusInternalServerError)return}w.WriteHeader(http.StatusOK)
}
2.5、测试验证
新增一个 todo.http 文件,内容如下:
# GET request to retrieve all todo itemsGET http://localhost:3000/api/todoitems HTTP/1.1#### GET request to retrieve a specific todo item by idGET http://localhost:3000/api/todoitems/20 HTTP/1.1#### GET request to retrieve all completed todo itemsGET http://localhost:3000/api/todoitems/complete HTTP/1.1 #### POST request to create a new todo itemPOST http://localhost:3000/api/todoitems HTTP/1.1Content-Type: application/json {"name": "Buy grocerie1"
}#### PUT request to update an existing todo itemPUT http://localhost:3000/api/todoitems/20 HTTP/1.1Content-Type: application/json{"name": "Buy groceries","is_complete": true
}#### DELETE request to delete a todo itemDELETE http://localhost:3000/api/todoitems/2 HTTP/1.1
完工了。
相关文章:
使用 Go 构建一个最小的 API 应用
最近有项目要使用 Go 开发,作为一个. NET Core 选手,准备先撸一个包含 CRUD 的最小 MVP 项目练手。 要创建一个 TODO 应用,会创建下面这些接口: APIDescriptionRequest bodyResponse bodyGET /todoitemsGet all to-do itemsNone…...
MySQL 日常维护指南:常见任务、频率及问题解决
MySQL 作为一种广泛使用的开源关系型数据库,随着数据量和应用复杂性的增加,定期的数据库维护对于保持系统高效运行至关重要。通过合理的日常维护,数据库管理员能够确保 MySQL 数据库的稳定性、性能以及数据的完整性。本文将介绍 MySQL 的常见…...
oracle ORA-24920:列大小对于客户机过大
问题描述 在一次读取某个视图数据过程中,当数据读取到x条时,报错ORA-24920:列大小对于客户机过大。 通过查询资料得知,oracle 数据库升级到了12c,VARCHAR2的容量也从4000升级到了32767。 所以猜测某个字段的长度超过4…...
使用 Docker compose 部署 Nacos(达梦数据库)
1. 制作镜像的源码地址 https://github.com/wangsilingwsl/nacos-dm.git 参考的开源项目:https://github.com/jeecgboot/JeecgBoot/tree/master/jeecg-boot/jeecg-server-cloud/jeecg-cloud-nacos (master分支;tag:v3.7.1&#…...
人工智能 | 阿里通义千问大模型
简介 通义千问系列模型为阿里云研发的大语言模型。千问模型基于 Transformer 架构,在超大规模的预训练数据上进行训练得到。预训练数据类型多样,覆盖广泛,包括大量网络文本、专业书籍、代码等。同时,在预训练模型的基础之上&…...
Windows环境下Qt Creator调试模式下qDebug输出中文乱码问题
尝试修改系统的区域设置的方法: 可以修复问题。但会出现其它问题: 比如某些软件打不开,或者一些软件界面的中文显示乱码! 暂时没有找到其它更好的办法。...
java防止表单重复提交的注解@RepeatSubmit
代码解释 RepeatSubmit 是一个自定义注解,通常用于防止表单重复提交。这个注解可以应用于控制器方法上,以确保同一个请求在一定时间内不会被多次提交。以下是一些常见的参数和用法: value: 注解的名称或描述。 interval: 两次请求之间的最小间…...
HTTP快速入门
HTTP报文结构 HTTP 协议主要由三大部分组成: ● 起始行(start line):描述请求或响应的基本信息; ● 头部字段(header):使用 key-value 形式更详细地说明报文; ● 消息正…...
Nacos简介
Nacos是一个开源的动态服务发现、配置管理和服务管理平台,由阿里巴巴集团开发并开源。它提供了服务注册与发现、配置管理、动态DNS服务、服务健康监测、权重和流量管理等核心特性,非常适合构建云原生应用和微服务架构。 Nacos的核心功能包括:…...
基于深度学习的稳健的模型推理与不确定性建模
基于深度学习的稳健模型推理与不确定性建模,是现代AI系统中至关重要的研究方向。随着深度学习在各类应用中的成功,如何保证模型在面对未知或不确定性输入时仍能做出稳健的推理,并能够量化这种不确定性,成为关键问题。稳健性与不确…...
C语言 sizeof 的介绍,以及sizeof计算数组名、 数组首地址、数组的元素之间的区别
一、sizeof 介绍 sizeof 是 C 语言中的一个运算符,用于计算数据类型或变量在内存中占用的字节数。用于计算数据类型或变量所占的内存大小,以字节为单位。它可以在编译时计算其操作数的大小,并返回一个 size_t 类型的值。它可以帮助了解不同类…...
深入理解Oracle闪回技术
引言: Oracle 闪回(Flashback)是一组强大的功能,用于恢复数据库中的数据或对象到过去的某个时间点或状态,而无需进行传统的基于备份和恢复的操作。 Oracle 闪回的主要类型 1. 闪回查询(Flashback Query&…...
Go 语言初探
Google 公司有一个传统,允许员工利用 20% 的工作时间开发自己的实验项目。2007 年 9月,UTF-8 的设计者之一 Rob Pike(罗布.皮克)在 Google 的分布式编译平台上进行 C++ 编译时,与同事 Robert Griesemer (罗布.格里泽默)在漫长的等待中讨论了编程语言面临的主要问题。他们一…...
使用ROS资源编排一键部署LNMP建站环境,手动整理教程
LNMP是目前主流的网站服务器架构之一,适合运行大型和高并发的网站应用,例如电子商务网站、社交网络、内容管理系统等。LNMP分别代表Linux、Nginx、MySQL和PHP。本文阿里云服务器网aliyunfuwuqi.com介绍如何使用阿里云资源编排服务(ROS&#x…...
猎板PCB镍钯金工艺你了解多少?
PCB镍钯金工艺,也称为ENEPIG(Electroless Nickel Electroless PALLADIum Gold)工艺,是一种在PCB表面处理中使用的先进工艺。这种工艺通过在PCB线路板上形成一层镍钯合金层,有效地提高了线路板的耐氧化性、耐腐蚀性和可…...
热更新解决方案2 —— Lua语法相关知识点
概述 开发环境搭建 Lua语法 1.第一个Lua程序 2.变量 print("******变量*******"); --lua当中的简单变量类型 -- nil number string boolean -- lua 中所有的变量声明 都不需要声明变量类型 它会自动的判断类型 -- 类似C# 中的var --lua中的一个变量 可以随便赋值 ——…...
【c++ arx选项板】
static void xlArx_gmenu(void) {if (!g_pPaletteSetEx){g_pPaletteSetEx=CTunnelSectionPaletteSetEx::Instance(...
新时代下吉林省城乡流动人才就业问题及路径探析
摘要:新时代背景下,中国经济快速发展,城乡融合发展成为缩小城乡差距,推动共同富裕的重要方式。吉林省作为东北老工业基地,传统产业竞争优势减弱,城乡流动人才就业规模增加,并呈现“农村-城市”的…...
Go 1.19.4 命令调用、日志、包管理、反射-Day 17
1. 系统命令调用 所谓的命令调用,就是通过os,找到系统中编译好的可执行文件,然后加载到内存中,变成进程。 1.1 exec.LookPath(寻找命令) 作用: exec.LookPath 函数用于在系统的环境变量中搜索可…...
Unity 2d UI 实时跟随场景3d物体
2d UI 实时跟随场景3d物体位置,显示 3d 物体头顶信息,看起来像是场景中的3dUI,实质是2d UIusing System.Collections; using System.Collections.Generic; using UnityEngine; using DG.Tweening; using UnityEngine.UI; /// <summary>…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...
