用gin写简单的crud后端API接口
提要
使用gin框架(go的web框架)来创建简单的几个crud接口)
使用技术: gin + sqlite3 + sqlx
创建初始工程
新建文件夹,创建三个子文件夹

分别初始化工程 go mod
如果没有.go文件,执行go mod tidy可能报错(warning: "all" matched no packages), 可以先不弄,只初始化模块就行(go mod init 模块名)

# 项目根目录创建模块
go mod init go_manager
go mod tidy
# 进入db目录
cd db
# 初始化模块
go mod init go_manager_db
go mod tidy
# 进入utils目录
cd ../utils
# 初始化模块
go mod init go_manager_utils
go mod tidy
# 进入web目录
cd ../web
# 初始化模块
go mod init go_manager_web
go mod tidy
go_manager_db模块编写
创建数据库连接(sqlite如果没有库会自动建)
// db\main.go
package go_manager_dbimport ("fmt""github.com/jmoiron/sqlx"_ "github.com/mattn/go-sqlite3"
)// 数据库相关操作
var db *sqlx.DB// 初始化数据库连接
func InitDB() (err error) {dsn := "./manager.db"// 连接// Open可能仅校验参数,而没有与db间创建连接,// 要确认db是否可用,需要调用Ping。Connect则相当于Open+Ping。db, err = sqlx.Connect("sqlite3", dsn)if err != nil {fmt.Printf("connect DB failed, err:%v\n", err)return}// 最大连接数db.SetMaxOpenConns(100)// 最大空闲连接数db.SetMaxIdleConns(16)// 初始化方法,建表+插入原始数据CreateRoleTable()CreateUserTable()return
}添加建表方法(初始化权限表和用户表)
// db\main.go
package go_manager_dbimport ("fmt""github.com/jmoiron/sqlx"_ "github.com/mattn/go-sqlite3"
)// 数据库相关操作
var db *sqlx.DB// 初始化数据库连接
func InitDB() (err error) {......}
// 创建用户表
func CreateUserTable() error {sqlc := `CREATE TABLE IF NOT EXISTS "mal_user" (-- sqlite 不能用 comment 添加注释"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT , -- '主键'"uname" varchar(20) NOT NULL UNIQUE , -- '用户昵称'"upass" varchar(50) NOT NULL, -- '密码(md5加密)'"rid" INTEGER NOT NULL UNIQUE DEFAULT 1 -- '角色id'); `_, err := db.Exec(sqlc)if err != nil {fmt.Println(err)return err}// 初始化表//因为有unique约束,所以不会重复添加// sqlStr := "insert into mal_user(uname,upass,rid) values(?,?,?)"Insert("mal_user", []string{"uname", "upass", "rid"}, "admin", "e120012d113ff6ea124a2493453c6dd5", 2)return nil
}// 创建权限表
func CreateRoleTable() error {sqlc := `CREATE TABLE IF NOT EXISTS "mal_role" ("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, -- '主键' "role" varchar(20) NOT NULL UNIQUE DEFAULT 'user' -- '角色(权限)' ); `_, err := db.Exec(sqlc)if err != nil {return err}// 初始化表// 因为有unique约束,所以不会重复添加 // 有四种权限,id(自增)越大代表权限越大,root>super>admin>userInsert("mal_role", []string{"role"}, "user")Insert("mal_role", []string{"role"}, "admin")Insert("mal_role", []string{"role"}, "super")Insert("mal_role", []string{"role"}, "root")return nil
}base.go: 通用插入和删除方法
// db\base.go
package go_manager_dbimport ("fmt"_ "github.com/mattn/go-sqlite3"utils "go_manager_utils"
)// 插入数据
func Insert(tableName string, params []string, datas ...interface{}) (err error) {// 拼接 表名(参数1,参数2,...)paramStr := utils.ParamsStr(params)// 拼接 values(?,?,...)values := utils.ValueStr(len(params))var sqlStr = "insert into " + tableName + paramStr + " values" + valuesfmt.Println(sqlStr)_, err = db.Exec(sqlStr, datas...) // 要用...展开if err != nil {fmt.Println(err)fmt.Println("插入数据失败")return}return
}// 删除数据
func Delete(tableName string, id int64) (err error) {sqlStr := "delete from " + tableName + " where id=?"fmt.Println(sqlStr)_, err = db.Exec(sqlStr, id)if err != nil {fmt.Println("删除数据失败")return}return
} model.go: 定义数据表对应的结构体
package go_manager_db// 专门定义与数据库交互的结构体// 用户表
type MalUser struct {Id int64 `db:"id" json:"Rd"`Uname string `db:"uname" json:"Uname"`Upass string `db:"upass" json:"Upass"`Rid int64 `db:"rid" json:"Rid"`
}
// 角色表
type MalRole struct {Id int64 `db:"id" json:"Id"`Role string `db:"role" json:"Role"`
}mal_user.go和mal_role.go: 定义用户表和角色表的crud方法
mal_user.go
package go_manager_dbimport ("fmt" utils "go_manager_utils"_ "github.com/mattn/go-sqlite3"
)// 查数据
func GetAllUser() (users []*MalUser, err error) {sqlStr := `select * from mal_user`// 查询,记录到booklisterr = db.Select(&users, sqlStr)if err != nil {fmt.Println("查询信息失败")fmt.Println(err)return}return
}// 根据id查数据
func GetUserById(id int64) (user MalUser, err error) {// 如果返回的是指针,需要初始化//book=&Book{}sqlStr := "select * from mal_user where id=?"err = db.Get(&user, sqlStr, id)if err != nil {fmt.Println("查询信息失败")return}return
}// 根据name查数据
func GetUserByName(uname string, upass string) (user MalUser, err error) {sqlStr := "select * from mal_user where uname=? and upass=?"err = db.Get(&user, sqlStr, uname, upass)if err != nil {fmt.Println("查询信息失败")return}return
}// 根据id改
func UptUserById(uid string, params []string, datas ...interface{}) (err error) {// 拼接参数列表 xxx=?,xxx=?paramsStr := utils.UptParamsStr(params)// uid直接传字符串拼接sqlStr := "update mal_role set " + paramsStr + " where id=" + uid_, err = db.Exec(sqlStr, datas...)if err != nil {fmt.Println("修改信息失败")return}return
}mal_role.go
package go_manager_dbimport ("fmt"_ "github.com/mattn/go-sqlite3"
)// 应该id越大,权限越高,比较方便区分权限
// user < admin < super < root
// 查数据
func GetAllRole() (roles []*MalRole, err error) {sqlStr := `select * from mal_role`// 查询,记录到booklisterr = db.Select(&roles, sqlStr)if err != nil {fmt.Println("查询信息失败")fmt.Println(err)return}return
}// 根据id查数据
func GetRoleById(id int64) (role MalRole, err error) {// 如果返回的是指针,需要初始化//book=&Book{}sqlStr := "select * from mal_role where id=?"err = db.Get(&role, sqlStr, id)if err != nil {fmt.Println("查询信息失败")return}return
}// 根据id改数据
func UptRoleById(id int64, roleName string) (err error) {// 如果返回的是指针,需要初始化//book=&Book{}sqlStr := "update mal_role set role=? where id=?"_, err = db.Exec(sqlStr, roleName, id)if err != nil {fmt.Println("修改信息失败")return}return
}引入项目里的其他模块: utils
在go.mod末尾添加
replace go_manager_utils => ../utils运行 go mod tidy


go_manager_utils模块编写
jwt.go: 编写加密方法,定时销毁token方法
package go_manager_utilimport ("crypto/md5""fmt""gopkg.in/square/go-jose.v2""gopkg.in/square/go-jose.v2/jwt""time"
)// sign 签名
// 传入密码,加密
func SignJWT(secret string, uname string, upass string) (jwtStr string) {key := []byte(secret)fmt.Println(secret)sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key},(&jose.SignerOptions{}).WithType("JWT"))if err != nil {panic(err)}cl := jwt.Claims{// Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐// 比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。Issuer: uname,Subject: upass,NotBefore: jwt.NewNumericDate(time.Now()),Audience: jwt.Audience{"name", "admin"},}raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()if err != nil {panic(err)}// fmt.Println(raw)return raw
}// 解析jwt
// 传入key(之前加密的密码),raw(jwt令牌)
func ParseJWT(key string, raw string) {var sharedKey = []byte(key)tok, err := jwt.ParseSigned(raw)if err != nil {panic(err)}out := jwt.Claims{}// 解析出issuer(uname)和subject(upass),校验if err := tok.Claims(sharedKey, &out); err != nil {panic(err)}fmt.Printf("iss: %s, sub: %s\n", out.Issuer, out.Subject)
}// DM5加密
func MD5(str string) string {data := []byte(str) //切片has := md5.Sum(data)md5str := fmt.Sprintf("%x", has) //将[]byte转成16进制return md5str
}// 销毁TokenMap的方法
// 定时销毁token(默认2小时)
func DestoryTokenMap(tokenMap map[string]string) {for k := range tokenMap {delete(tokenMap, k)}
}myTime.go: 定义定时器方法
package go_manager_utilimport ( "time"
)// 定义函数类型
type Fn func() error// 定时器中的成员
type MyTicker struct {MyTick *time.TickerRunner Fn
}
type MyTimer struct {MyTime *time.TimerRunner Fn
}func NewMyTick(interval int, f Fn) *MyTicker {return &MyTicker{MyTick: time.NewTicker(time.Duration(interval) * time.Second),Runner: f,}
}// 一次性
func NewMyTimer(interval int, f Fn) *MyTimer {return &MyTimer{MyTime: time.NewTimer(time.Duration(interval) * time.Second),Runner: f,}
}// 启动定时器需要执行的任务
func (t *MyTicker) Start() {for {select {case <-t.MyTick.C:t.Runner()}}
}// 启动定时器需要执行的任务
func (t *MyTimer) Start() { select {case <-t.MyTime.C:t.Runner()}
}// func over() error {
// fmt.Println("token过期")
// return nil
// }
// 测试
// func main() {
// t := NewMyTimer(2, over)
// t.Start()
// }res.go: 响应前端请求的方法
package go_manager_utilimport ("fmt""github.com/gin-gonic/gin""net/http"
)/* 通用响应方法 */
func R(c *gin.Context, err error, msg interface{}, data interface{}) {// 如果有err,就说明是有错误,就返回错误响应(msg)if err != nil {fmt.Println(err)c.JSON(http.StatusInternalServerError, gin.H{"status": 500,"msg": msg,})return}// 返回正确响应(data)c.JSON(http.StatusOK, gin.H{"status": 200,"msg": data,})
}stringUtils.go: 封装字符串操作方法
package go_manager_utils// 拼接sql语句的value
// len是语句有几个参数
func ValueStr(len int) (values string) {// 拼接 values(?,?,...)values = "("for i := 0; i < len-1; i++ {values += "?"values += ","}values += "?"values += ")"return
}// 拼接sql语句update的param
// params是参数名数组
func UptParamsStr(params []string) (paramStr string) {// 拼接参数列表 xxx=?,xxx=?paramStr = ""for i := 0; i < len(params)-1; i++ {paramStr += params[i]paramStr += "=?,"}paramStr += params[len(params)-1]paramStr += "=?"return
}// 拼接sql语句的param
// params是参数名数组
func ParamsStr(params []string) (paramStr string) {// 拼接 表名(参数1,参数2,...)paramStr = "("for i := 0; i < len(params)-1; i++ {paramStr += params[i]paramStr += ","}paramStr += params[len(params)-1]paramStr += ")"return
}运行go mod tidy处理go文件里的依赖

go_manager_web模块编写
main.go: 主要逻辑,创建web实例,注册路由...
package go_manager_webimport ("fmt""github.com/gin-gonic/gin"db "go_manager_db"utils "go_manager_utils""net/http"
)// 定义路由组
// 组中组(嵌套路由组)
func DefineRouteGroup(fatherGroup *gin.RouterGroup, groupName string, r *gin.Engine) *gin.RouterGroup {var group *gin.RouterGroupif fatherGroup != nil {// v1/groupNamegroup = fatherGroup.Group(groupName)} else {// /groupNamegroup = r.Group(groupName)}// 返回路由组return group
}// 存放 token (不同ip不同token)
var TokenMap = make(map[string]string, 10)// 定时销毁token
func timeDT() {// 两小时后销毁t := utils.NewMyTimer(2*60*60, func() error {utils.DestoryTokenMap(TokenMap)return nil})t.Start()fmt.Println(TokenMap)
}// 路由和处理函数放在不同文件好像会使中间件失效
func Login(c *gin.Context) { user := db.MalUser{}// 绑定json和结构体(接收json,数据放入结构体)if err := c.BindJSON(&user); err != nil {return}uname := user.Unameupass := user.Upass userModel, err := db.GetUserByName(uname, upass)if err != nil || &userModel == nil {fmt.Println(err)c.JSON(500, gin.H{"status": 500,"msg": "登录失败",})return} token := utils.SignJWT("malred", uname, upass)// 存入map// fmt.Println(c.ClientIP(),c.RemoteIP())TokenMap[c.ClientIP()] = tokenfmt.Println(TokenMap)c.JSON(http.StatusOK, gin.H{"status": 200,"msg": "登录成功",// 返回jwt令牌(密码因为前端md5加密过,所以直接放入jwt)"token": token,})go timeDT()
}// 路由器
// 启动默认的路由
var r = gin.Default()// user路由组
var v1 *gin.RouterGroupfunc Run() {// 使用中间件// 日志r.Use(gin.Logger())// 错误恢复r.Use(gin.Recovery())// 跨域r.Use(Core())// 阻止缓存响应r.Use(NoCache())// 安全设置r.Use(Secure())// 创建路由组v1v1 = DefineRouteGroup(nil, "v1", r)v1.POST("login", Login)// 注册user的路由registerUser(Token(), Core())// 注册role的路由registerRole(Token(), Core())// 启动webserver,监听本地127.0.0.1(默认)端口r.Run(":10101")
}moddilewares.go: 中间件
package go_manager_webimport ( utils "go_manager_utils""net/http""strconv""time""github.com/gin-gonic/gin"
)//解决跨域问题
func Core() gin.HandlerFunc {return func(c *gin.Context) {method := c.Request.Methodc.Header("Access-Control-Allow-Origin", "*")c.Header("Access-Control-Allow-Headers", "*")c.Header("Access-Control-Allow-Methods", "*")c.Header("Access-Control-Expose-Headers", "Content-Length,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Content-Type")c.Header("Access-Control-Max-Age", "3600")c.Header("Access-Control-Allow-Credentials", "true")//放行索引optionsif method == "OPTIONS" {c.AbortWithStatus(http.StatusNoContent)}//处理请求c.Next()}
}// 权限认证(验证token)
func Token() gin.HandlerFunc {return func(c *gin.Context) {// for k, v := range c.Request.Header {// fmt.Println(k, v)// }secret := c.Request.Header["Secret"] // 获取前端传来的secrettoken := c.Request.Header["Token"]if len(token) == 0 {// 验证不通过,不再调用后续的函数处理c.Abort()c.JSON(http.StatusUnauthorized, gin.H{"code": 401,"message": "访问未授权",})return}timeInt64 := strconv.FormatInt(time.Now().UnixNano()/1e6/1000/60, 10)md5Str := utils.MD5(timeInt64 + TokenMap[c.ClientIP()])// fmt.Println(TokenMap[c.ClientIP()], timeInt64)// fmt.Println(timeInt64 + TokenMap[c.ClientIP()])// fmt.Println(md5Str, secret[0])if md5Str != secret[0] {// 验证不通过,不再调用后续的函数处理c.Abort()c.JSON(http.StatusUnauthorized, gin.H{"code": 401,"message": "访问未授权",})return}// 验证jwt// utils.ParseJWT(secret[0][8:11]+secret[0][19:22], token[0])//处理请求c.Next()}
}// 阻止缓存响应
func NoCache() gin.HandlerFunc {return func(ctx *gin.Context) {ctx.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")ctx.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")ctx.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))ctx.Next()}
}// 响应 options 请求, 并退出
// func Options() gin.HandlerFunc {
// return func(ctx *gin.Context) {
// if ctx.Request.Method != "OPTIONS" {
// ctx.Next()
// } else {
// ctx.Header("Access-Control-Allow-Origin", "*")
// ctx.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
// ctx.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
// ctx.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
// ctx.Header("Content-Type", "application/json")
// ctx.AbortWithStatus(200)
// }
// }
// }// 安全设置
func Secure() gin.HandlerFunc {return func(ctx *gin.Context) {ctx.Header("Access-Control-Allow-Origin", "*")ctx.Header("X-Frame-Options", "DENY")ctx.Header("X-Content-Type-Options", "nosniff")ctx.Header("X-XSS-Protection", "1; mode=block")if ctx.Request.TLS != nil {ctx.Header("Strict-Transport-Security", "max-age=31536000")}// Also consider adding Content-Security-Policy headers// ctx.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")}
}// todo 权限控制(token携带当前用户的权限信息,过滤低于指定权限的请求)role.go和user.go: 真的role和user表的web操作
role.go
package go_manager_webimport ("fmt"db "go_manager_db"utils "go_manager_utils""strconv" "github.com/gin-gonic/gin"
)func GetAllRoleHandler(c *gin.Context) {roles, err := db.GetAllRole()// 通用响应utils.R(c, err, "获取角色列表失败", roles)
}
func AddRoleHandler(c *gin.Context) {// Role := c.PostForm("Role")// fmt.Println(Role)role := db.MalRole{}//绑定json和结构体if err := c.BindJSON(&role); err != nil {return}Role := role.Roleerr := db.Insert("mal_role", []string{"role"}, Role)// 通用响应utils.R(c, err, "添加角色失败", "添加角色成功")
}
func DelRoleHandler(c *gin.Context) {// 从url获取参数idStr := c.Query("rid")// fmt.Println(idStr)rid, err := strconv.ParseInt(idStr, 10, 64)err = db.Delete("mal_role", rid)// 通用响应utils.R(c, err, "删除角色失败", "删除角色成功")
}
func GetOneRoleHandler(c *gin.Context) {// 从url获取参数idStr := c.Query("rid")fmt.Println(idStr)rid, _ := strconv.ParseInt(idStr, 10, 64)one, err2 := db.GetRoleById(rid)// 通用响应utils.R(c, err2, "查询角色失败", one)
}
func UptRoleHandler(c *gin.Context) {role := db.MalRole{}//绑定json和结构体if err := c.BindJSON(&role); err != nil {return} rid := role.IdroleName := role.Rolefmt.Println(role)err := db.UptRoleById(rid, roleName)// 通用响应utils.R(c, err, "修改角色失败", "修改角色成功")
}
func registerRole(middles ...gin.HandlerFunc) {// 创建路由组v1/userrole := DefineRouteGroup(v1, "role", r)// 添加中间件if middles != nil {role.Use(middles...)}// 获取所有role.GET("all", GetAllRoleHandler)// 添加role.POST("add", AddRoleHandler)// 删除role.DELETE("del", DelRoleHandler)// 根据id获取role.GET("id", GetOneRoleHandler)// 根据id修改role.PUT("upt", UptRoleHandler)
}user.go
package go_manager_webimport ("fmt"db "go_manager_db"utils "go_manager_utils""strconv" "github.com/gin-gonic/gin"
)func GetAllUserHandler(c *gin.Context) {users, err := db.GetAllUser()// 通用响应utils.R(c, err, "查询角色失败", users)
}
func AddUserHandler(c *gin.Context) {// uname := c.PostForm("uname")// upass := c.PostForm("upass")// idStr := c.PostForm("rid")user := db.MalUser{}//绑定json和结构体if err := c.BindJSON(&user); err != nil {return}uname := user.Unameupass := user.Upassrid := user.Ridfmt.Println(user)// rid, err := strconv.ParseInt(idStr, 10, 64)err := db.Insert("mal_user", []string{"uname", "upass", "rid"}, uname, upass, rid)// 通用响应utils.R(c, err, "添加角色失败", "添加角色成功")
}
func DelUserHandler(c *gin.Context) {// 从url获取参数idStr := c.Query("uid")// fmt.Println(idStr)uid, err := strconv.ParseInt(idStr, 10, 64)err = db.Delete("mal_user", uid)// 通用响应utils.R(c, err, "删除角色失败", "删除角色成功")
}
func GetOneUserHandler(c *gin.Context) {// 从url获取参数idStr := c.Query("uid")fmt.Println(idStr)uid, _ := strconv.ParseInt(idStr, 10, 64)one, err2 := db.GetUserById(uid)// 通用响应utils.R(c, err2, "查询角色失败", one)
}
func UptUserHandler(c *gin.Context) {// 从url获取参数// uid := c.PostForm("uid")// uname := c.PostForm("uname")// upass := c.PostForm("upass")// ridStr := c.PostForm("rid")user := db.MalUser{}//绑定json和结构体if err := c.BindJSON(&user); err != nil {return}uname := user.Unameupass := user.Upassrid := user.Riduid := user.Id// fmt.Println(idStr, UserName)// rid, _ := strconv.ParseInt(ridStr, 10, 64)err := db.UptUserById(strconv.FormatInt(uid, 10), []string{"uname", "upass", "rid"}, uname, upass, rid)// 通用响应utils.R(c, err, "修改角色失败", "修改角色成功")
}
func registerUser(middles ...gin.HandlerFunc) {// 创建路由组v1/useruser := DefineRouteGroup(v1, "user", r)// 添加中间件if middles != nil {user.Use(middles...)}user.GET("all", GetAllUserHandler)// 添加user.POST("add", AddUserHandler)// 删除user.DELETE("del", DelUserHandler)// 根据id获取user.GET("id", GetOneUserHandler)// 根据id修改user.PUT("upt", UptUserHandler)
}运行go mod tidy

忘了,要引用项目里的其他包
replace go_manager_utils => ../utils
replace go_manager_db => ../dbgo mod tidy

编写根目录的go_manager模块
main.go
package mainimport ( db "go_manager_db"web "go_manager_web"
)func main() {// 初始化数据库db.InitDB() // 开启服务web.Run()
}go.mod
module go_managergo 1.18replace go_manager_web => ./webreplace go_manager_db => ./dbreplace go_manager_utils => ./utilsgo mod tidy

测试(可以用go build打包)
完整目录结构

go run main.go

因为后端存的密码是md5加密过的,所以前端也要传md5加密的密码,二者相同才能通过

安全: 我的安全不咋地,加密的方法是前端根据当前时间戳(转为分钟,防止因为前后端延迟而导致时间戳不一致)+登录后从后端获取的token来md5,每次请求都会验证这个md5(后端也加密(时间戳/60+token)然后对比),这个就不测试了
代码仓库:
https://gitee.com/malguy/go-manager
配套前端管理系统(react18):
https://github.com/malred/base-manager


相关文章:
用gin写简单的crud后端API接口
提要使用gin框架(go的web框架)来创建简单的几个crud接口)使用技术: gin sqlite3 sqlx创建初始工程新建文件夹,创建三个子文件夹分别初始化工程 go mod如果没有.go文件,执行go mod tidy可能报错(warning: "all" matched no packages), 可以先不弄,只初始化模块就行(…...
CF大陆斗C战士(三)
文章目录[C. Good Subarrays](https://codeforces.com/problemset/problem/1398/C)题目大意题目分析code[C. Boboniu and Bit Operations](https://codeforces.com/problemset/problem/1395/C)题目大意题目分析code[C. Rings](https://codeforces.com/problemset/problem/1562/…...
TTS | 语音合成论文概述
综述系列2021_A Survey on Neural Speech Synthesis论文:2106.15561.pdf (arxiv.org)论文从两个方面对神经语音合成领域的发展现状进行了梳理总结(逻辑框架如图1所示):核心模块:分别从文本分析(textanalysi…...
HTML第5天 HTML新标签与特性
新标签与特性文档类型设定前端复习帮手W3Schoool常用新标签datalist标签,与input元素配合,定义选项列表fieldset元素新增input表单文档类型设定 document – HTML: 开发环境输入html:4s – XHTML: 开发环境输入html:xt – HTML5: 开发环境输入html:5 前…...
java ee 之进程
目录 1.进程的概念 2.进程管理 3.进程属性(pcb) 3.1pid 3.2内存指针 3.3文件描述符 3.4进程调度 3.4.1进程状态 3.4.2 进程的优先级 3.4.3进程的上下文 3.4.4进程的记账信息 5.进程间通信 1.进程的概念 一个运行起来的程序,就是进程 .exe是一个可执行文件(程序),双…...
Linux学习记录——십사 进程控制(1)
文章目录1、进程创建1、fork函数2、进程终止1、情况分类2、如何理解进程终止3、进程终止的方式3、进程等待1、进程创建 1、fork函数 fork函数从已存在进程中创建一个新进程,新进程为子进程,原进程为父进程。 #include <unistd.h> pid_t fork(vo…...
使用 create-react-app 脚手架搭建React项目
❀官网 1、安装脚手架:npm install -g create-react-app 2、查看版本:create-react-app -V !!!注意 Node版本必须是14以上,不然会报以下错误。 3、创建react项目(项目名不能包含大写字母&…...
inquirerjs
inquirerjs inquirerjs是一个用来实现命令行交互界面的工具集合。它帮助我们实现与用户的交互交流,比如给用户一个提醒,用户给我们一个答案,我们根据用户的答案来做一些事情,典型应用如plop等生成器工具。 npm install inquirer…...
[数据库]内置函数
●🧑个人主页:你帅你先说. ●📃欢迎点赞👍关注💡收藏💖 ●📖既选择了远方,便只顾风雨兼程。 ●🤟欢迎大家有问题随时私信我! ●🧐版权:本文由[你帅…...
shell基本知识
为什么学习和使用Shell编程 什么是Shell shell的起源 shell的功能 shell的分类 如何查看当前系统支持的shell? 如何查看当前系统默认shell? 驼峰语句 shell脚本的基本元素 shell脚本编写规范 shell脚本的执行方式 shell脚本的退出状态 …...
Http长连接和短连接
http1.0以前,默认使用的是短连接,客户端与服务器之间每进行一次http操作,就会建立一次连接,例如,打开一个网页,包括html文件,js,css,每获取一次资源,就需要进…...
[SQL Statements] 基本的SQL知识 之DDL针对表结构和表空间的基本操作
[SQL Statements] 基本的SQL知识 之DDL针对表结构和表空间的基本操作 什么是数据库的表以及表空间 在MySQL中,一个数据库可以包含多个表,每个表是由若干个列(column)和行(row)组成的。表是存储数据的基本…...
Git版本控制工具(详解)
Git版本控制工具 Git常见命令速查表 集中式版本控制 cvs和svn都是属于集中式版本控制系统 他们的主要特点是单一的集中管理服务器 保存所有文件的修订版本协同开发人员通过客户端连接到这台服务器 取出最新的文件或者提交更新 优点每个人都可以在一定程度上看到项目中的其他…...
408考研计算机之计算机组成与设计——知识点及其做题经验篇目2:指令系统
今天我们来讲一讲指令系统里面的知识点以及做题技巧 1、定义 考点1:指令定义 指令是指示计算机执行某种操作的命令,一台计算机的所有指令的集合构成该机的指令系统,也称为指令集。指令系统是指令集体系结构ISA中最核心的部分,ISA…...
Java语法中的方法引用::是个什么鬼?
1.函数式接口 函数式接口(Functional Interface)就是一个有且仅有一个抽象方法(通俗来说就是只有一个方法要去被实现,因此我们也能通过这个去动态推断参数类型),但是可以拥有多个非抽象方法的接口。函数式接…...
【使用vue init和vue create的区别以及搭建vue项目的教程】
vue init 是vue-cli2.x的初始化方式,可以使用github上面的一些模板来初始化项目 webpack是官方推荐的标准模板名 使用方式:vue init webpack 项目名称 例如使用github上面electron-vue的模板使用方式:vue init electron-vue 项目名称教程目…...
二、HTTP协议02
文章目录一、HTTP状态管理Cookie和Session二、HTTP协议之身份认证三、HTTP长连接与短连接四、HTTP中介之代理五、HTTP中介之网关六、HTTP之内容协商七、断点续传和多线程下载一、HTTP状态管理Cookie和Session HTTP的缺陷无状态。Cookie和Session就用来弥补这个缺陷的。 Cooki…...
免费Api接口汇总(亲测可用,可写项目)
免费Api接口汇总(亲测可用)1. 聚合数据2. 用友API3. 天行数据4. Free Api5. 购物商城6. 网易云音乐API7. 疫情API8. 免费Api合集1. 聚合数据 https://www.juhe.cn/ 2. 用友API http://iwenwiki.com/wapicovid19/ 3. 天行数据 https://www.tianapi.com…...
12.并发编程
1.并发并发:逻辑流在时间时重叠构造并发程序:进程:每个逻辑控制流是一个进程,由内核调度和维护进程有独立的虚拟地址空间,想要通信,控制流必须使用某种显式的进程间通信机制(IPC)I/O多路复用:程…...
C/C++指针与数组(一)
预备知识 1、数据的存储 2、基本内建类型 1)类型的大小 C offers a flexible standard with some guaranteed minimum sizes, which it takes from C: A short integer is at least 16 bits wide.An int integer is at least as big as short.A long integer is a…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
API网关Kong的鉴权与限流:高并发场景下的核心实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中,API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关,Kong凭借其插件化架构…...
