用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…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...
Python实现简单音频数据压缩与解压算法
Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中,压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言,提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...
