用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…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...
[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG
TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码:HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...
数据结构:递归的种类(Types of Recursion)
目录 尾递归(Tail Recursion) 什么是 Loop(循环)? 复杂度分析 头递归(Head Recursion) 树形递归(Tree Recursion) 线性递归(Linear Recursion)…...
WEB3全栈开发——面试专业技能点P7前端与链上集成
一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染(SSR)与静态网站生成(SSG) 框架,由 Vercel 开发。它简化了构建生产级 React 应用的过程,并内置了很多特性: ✅ 文件系…...
在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7
在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤: 第一步: 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为: // 改为 v…...
从零开始了解数据采集(二十八)——制造业数字孪生
近年来,我国的工业领域正经历一场前所未有的数字化变革,从“双碳目标”到工业互联网平台的推广,国家政策和市场需求共同推动了制造业的升级。在这场变革中,数字孪生技术成为备受关注的关键工具,它不仅让企业“看见”设…...
