美食美客网站建设项目规划书/服务网站推广方案
Gin框架
- Gin简介
- 第一个Gin示例Helloworld
- RESTful API
- Gin返回数据的几种格式
- Gin 获取参数
- HTTP重定向
- Gin路由&路由组
- Gin框架当中的中间件
Gin简介
Gin 是一个用 Go (Golang) 编写的 web 框架。它是一个类似于 martini 但拥有更好性能的 API 框架,由于 httprouter,速度提高了近 40 倍。Gin在GitHub上已经有47k的star,它和Golang的语法一样简洁明了,使得初学者得以迅速入门。只需要在终端上输入以下命令就可以将使用gin框架了
go get -u github.com/gin-gonic/gin
第一个Gin示例Helloworld
import ("github.com/gin-gonic/gin""net/http"
)func main() {router := gin.Default()//默认引擎router.GET("/", func(c *gin.Context) {c.String(http.StatusOK, "Hello World")})router.Run(":8000")//启动服务
}
下面解释一下这个上面这份代码的意思
- router:=gin.Default():这是默认的服务器。使用gin的Default方法创建一个路由Handler
- 然后通过Http方法绑定路由规则和路由函数。不同于net/http库的路由函数,gin进行了封装,把request和response都封装到了gin.Context的上下文环境中。
- 最后启动路由的Run方法监听端口。还可以用http.ListenAndServe(“:8080”, router),或者自定义Http服务器配置。
启动方式有如下两种:
// 启动方式一
router.Run(":8000")
// 启动方式二
http.ListenAndServe(":8000", router)
注意这个 :8080的意思其实是这个127.0.0.1:8080这一点大家需要注意了。当然我们也可以将这个**“:8080"改为这个"0.0.0.0:8080”**.
package mainimport ("github.com/gin-gonic/gin""net/http"
)func Index(context *gin.Context) {context.String(http.StatusOK, "Hello ksy!")
}
func main() {// 创建一个默认的路由router := gin.Default()// 绑定路由规则和路由函数,访问/index的路由,将由对应的函数去处理router.GET("/index", Index)// 启动监听,gin会把web服务运行在本机的0.0.0.0:8080端口上router.Run("0.0.0.0:8080")// 用原生http服务的方式, router.Run本质就是http.ListenAndServe的进一步封装http.ListenAndServe(":8080", router)
}
在这里博主在说一下这个Index这个函数的参数是固定写死的就是确定的他的类型必须是这个gin.Context。上面的这个http.StatusOk代表的是这个状态码在这里代表的是这个200*
下面我们将上面这个程序跑起来,然后打开浏览器访问/index这个路径我看一下这个效果
RESTful API
REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”或“表现层状态转化”。
简单来说,REST的含义就是客户端与Web服务器之间进行交互的时候,使用HTTP协议中的4个请求方法代表不同的动作。
- GET用来获取资源
- POST用来新建资源
- PUT用来更新资源
- DELETE用来删除资源。
Gin框架支持开发RESTful API的开发。
func main() {r := gin.Default()r.GET("/book", func(c *gin.Context) {c.JSON(200, gin.H{"message": "GET",})})r.POST("/book", func(c *gin.Context) {c.JSON(200, gin.H{"message": "POST",})})r.PUT("/book", func(c *gin.Context) {c.JSON(200, gin.H{"message": "PUT",})})r.DELETE("/book", func(c *gin.Context) {c.JSON(200, gin.H{"message": "DELETE",})})
}
其中这个c.JSON代表的是返回的是一个json格式的数据,而这个gin.H其实是一个这个Map。我们可以查看其定义。
// H is a shortcut for map[string]interface{}
type H map[string]any
当然我们也可以不使用这个gin框架给我提供的使用我们自己定义的。
package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.GET("/json", func(c *gin.Context) {//方法1使用map,方法二使用结构体data := map[string]interface{}{"name": "小王子","message": "ksy","age": 18,}var msg struct { //注意这个结构体里面的字段必须大写,因为golang当中包的访问性Name string `json:"name"`Age int `json:"age"`Message string `json:"message"`}msg.Message = "ni hao ksy"msg.Age = 20msg.Name = "ksy"//获取使用gin.Hc.JSON(http.StatusOK, data)c.JSON(http.StatusOK, msg)})r.Run(":9090")
}
在这里我们需要注意的是这个结构体在进行序列化的时候这个首字母需要大写,赋值会序列化失败这是因为这个golang包的可见性决定的。
那会不会有这样一个场景了,就是某个路径Get,Post等方法都可以访问这个路径时我们又该如何来写了,或者是这个当用户返回这个服务器上的路径不存在时我们不希望返回这个404NotFound,我们希望返回我们这个我们自定义的东西时又该如何写了。gin框架已经提供给了我们这个处理的函数了下面我们一起来看看吧。
package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()//可以接受任何请求r.Any("usr", func(c *gin.Context) {switch c.Request.Method {//判断到底是那种请求case http.MethodGet:c.JSON(http.StatusOK, gin.H{"method": "get"})case http.MethodPost:c.JSON(http.StatusOK, gin.H{"method": "post"})}})//定义没有时执行的函数也就是用户访问的路径不存在时会调用这个函数r.NoRoute(func(c *gin.Context) {c.JSON(http.StatusNotFound, gin.H{"msg": "ksy.com",})})r.Run(":8081")
}
我们可以使用这个Any方法进而在其回调方法当中获取其请求的方试通过一个简单的switch,case进行不同的处理将数据返回。还有就是这个NoRoute方法就是当用户这个输入的路径不存在时会执行这个回调。下面我们来演示一下这个效果。
当这个请求路径不存在时。
Gin返回数据的几种格式
在gin框架当中我们可以指定这个返回数据的格式。在前面的例子当中我们这个String,json类型的数据我们都返回了下面我们一起看看这个剩下的格式吧
1.返回字符串类型的数据
router.GET("/index", func(c *gin.Context) {c.String(http.StatusOK, "hello world")
})
2.返回json类型数据
c.JSON(http.StatusOK, gin.H{"method": "get"})
3.返回xml数据格式
router.GET("/xml", func(c *gin.Context) {c.XML(http.StatusOK, gin.H{"user": "hanru", "message": "hey", "status": http.StatusOK})
})
4.返回yaml数据格式
router.GET("/yaml", func(c *gin.Context) {c.YAML(http.StatusOK, gin.H{"user": "hanru", "message": "hey", "status": http.StatusOK})
})
5.返回html数据格式
先要使用 **LoadHTMLGlob()或者LoadHTMLFiles()**方法来加载模板文件。
router.LoadHTMLGlob("gin框架/templates/*")
//router.LoadHTMLFiles("templates/index.html", "templates/index2.html")
//定义路由
router.GET("/html", func(c *gin.Context) {//根据完整文件名渲染模板,并传递参数c.HTML(http.StatusOK, "index.html", gin.H{"title": "hello world",})
})
在模板中使用这个title,需要使用{{ .title }}
不同文件夹下模板名字可以相同,此时需要 LoadHTMLGlob() 加载两层模板路径
router.LoadHTMLGlob("templates/**/*")
router.GET("/posts/index", func(c *gin.Context) {c.HTML(http.StatusOK, "posts/index.html", gin.H{"title": "Posts",})c.HTML(http.StatusOK, "users/index.html", gin.H{"title": "Users",})})
6.返回文件响应
// 在golang总,没有相对文件的路径,它只有相对项目的路径
// 网页请求这个静态目录的前缀, 第二个参数是一个目录,注意,前缀不要重复
router.StaticFS("/static", http.Dir("static/static"))
// 配置单个文件, 网页请求的路由,文件的路径
router.StaticFile("/titian.png", "static/titian.png")
Gin 获取参数
1.查询参数 Query也就是这个获取querystring参数,querystring指的是URL中?后面携带的参数,例如:/user/search?username=匡思源&address=沙河。 获取请求的querystring参数的方法如下:
package mainimport ("github.com/gin-gonic/gin""net/http"
)//queryString 通常用在Get方法func main() {r := gin.Default()r.GET("/get", func(c *gin.Context) {//Get请求URL?后面是querystring参数 key=val形势多个用&连接//获取json那边发请求携带的queryString//Name := c.Query("name")//Name:=c.DefaultQuery("name","ddd")//如果能查到用查到的否则用设置的默认值Name, _ := c.GetQuery("name") //返回值bool取不到返回falsec.JSON(http.StatusOK, gin.H{"name": Name,})})r.Run(":8089")
}
在这里获取这个这个请求参数querystring,这个gin提供了多种函数:
- GetQuery:如果参数不存在第二个返回值为false
- DefaultQuery:如果参数不存在返回默认设置的值,存在用传进来的值
- Query:获取参数不存在为""
下面我们将这个程序运行起来看一看这个效果如何
剩下的各位铁子可以自行下去演示这个效果,在这里博主就不一一演示这个效果了。
2.获取form参数
当前端请求的数据通过form表单提交时,例如向/user/search发送一个POST请求,获取请求数据的方式如下:
package mainimport ("github.com/gin-gonic/gin""net/http"
)/*获取form表单的提交参数
*/func main() {r := gin.Default()r.LoadHTMLFiles("./login.html") //加载某版文件r.GET("/login", func(c *gin.Context) {c.HTML(http.StatusOK, "login.html", nil)})r.POST("/login", func(c *gin.Context) {//获取form表单提交的数据// username:=c.PostForm("username")// password:=c.PostForm("password")password := c.DefaultPostForm("password", "****")username := c.DefaultPostForm("username", "somebody")//c.GetPostForm("username")两个返回值有一个为这个是否存在c.JSON(http.StatusOK, gin.H{"username": username,"password": password,})})r.Run("127.0.0.1:9091")
}
和这个QueryString是一样的这个gin提供了多种获取函数:
- PostForm:获取用户传过来的表单数据
- DefaultPostForm:如果用户没传使用设置的默认值
- GetPostForm:第二个返回值可以判断用户是否传递了这个参数
下面我们可以使用这个PostMan来进行这个测试
3.获取json数据
当前端请求的数据通过JSON提交时,例如向/json发送一个POST请求,则获取请求参数的方式如下:
package mainimport ("encoding/json""github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.POST("/json", func(c *gin.Context) {// 注意:下面为了举例子方便,暂时忽略了错误处理b, _ := c.GetRawData() // 从c.Request.Body读取请求数据// 定义map或结构体var m map[string]interface{}// 反序列化_ = json.Unmarshal(b, &m)c.JSON(http.StatusOK, m)})r.Run(":8081")
}
在这里我们同样的使用这个Postman来进行测试
4.获取获取path参数
请求的参数通过URL路径传递,例如:/user/search/小王子/沙河。 获取请求URL路径中的参数的方式如下。
package mainimport ("github.com/gin-gonic/gin""net/http"
)//获取请求path(URI)参数返回的都是字符串类型
func main() {r := gin.Default()r.GET("/:name/:age", func(c *gin.Context) {//获取路径参数name := c.Param("name")age := c.Param("age")c.JSON(http.StatusOK, gin.H{"name": name,"age": age,})})r.GET("/blog/:year/:month", func(c *gin.Context) {year := c.Param("year")month := c.Param("month")c.JSON(http.StatusOK, gin.H{"year": year,"month": month,})})r.Run(":9092")
}
这个在这里就不演示了,非常的简单各位铁子可以自行演示即可。
5.参数绑定(非常重要)
为了能够更方便的获取请求相关参数,提高开发效率,我们可以基于请求的Content-Type识别请求数据类型并利用反射机制自动提取请求中QueryString、form表单、JSON、XML等参数到结构体中。 下面的示例代码演示了.ShouldBind()强大的功能,它能够基于请求自动提取JSON、form表单和QueryString类型的数据,并把值绑定到指定的结构体对象。
// Binding from JSON
type Login struct {User string `form:"user" json:"user" binding:"required"`Password string `form:"password" json:"password" binding:"required"`
}func main() {router := gin.Default()// 绑定JSON的示例 ({"user": "q1mi", "password": "123456"})router.POST("/loginJSON", func(c *gin.Context) {var login Loginif err := c.ShouldBind(&login); err == nil {fmt.Printf("login info:%#v\n", login)c.JSON(http.StatusOK, gin.H{"user": login.User,"password": login.Password,})} else {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})}})// 绑定form表单示例 (user=q1mi&password=123456)router.POST("/loginForm", func(c *gin.Context) {var login Login// ShouldBind()会根据请求的Content-Type自行选择绑定器if err := c.ShouldBind(&login); err == nil {c.JSON(http.StatusOK, gin.H{"user": login.User,"password": login.Password,})} else {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})}})// 绑定QueryString示例 (/loginQuery?user=q1mi&password=123456)router.GET("/loginForm", func(c *gin.Context) {var login Login// ShouldBind()会根据请求的Content-Type自行选择绑定器if err := c.ShouldBind(&login); err == nil {c.JSON(http.StatusOK, gin.H{"user": login.User,"password": login.Password,})} else {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})}})// Listen and serve on 0.0.0.0:8080router.Run(":8080")
}
ShouldBind会按照下面的顺序解析请求中的数据完成绑定:
如果是 GET 请求,只使用 Form 绑定引擎(query)。
如果是 POST 请求,首先检查 content-type 是否为 JSON 或 XML,然后再使用 Form(form-data)功能非常的强大各位铁子需要这个重点看一下这个.
HTTP重定向
HTTP 重定向很容易。 内部、外部重定向均支持。下面我们一起来看看这个如何进行重定向
package mainimport ("github.com/gin-gonic/gin""net/http"
)//重定向未登录进行重定向
func main() {r := gin.Default()r.GET("/a", func(c *gin.Context) {//重定向//c.Redirect(http.StatusMovedPermanently, "https://www.baidu.com") //永久重定向c.Request.URL.Path = "/b"r.HandleContext(c) //继续处理})//路由重定向r.GET("/b", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "b",})})r.Run(":9093")
}
注意上面注释那个是永久重定向当用户第二次请求时不会再这个返回它了,而是直接访问重定向的网址。而下面那个时使用这个路由重定向,使用HandleContext。
Gin路由&路由组
我们之前的例子使用的是这个普通路由,就是类似于这个下面这样
r.GET("/index", func(c *gin.Context) {...})
r.GET("/login", func(c *gin.Context) {...})
r.POST("/login", func(c *gin.Context) {...})
此外,还有一个可以匹配所有请求方法的Any方法如下:
r.Any("/test", func(c *gin.Context) {...})
之前也已经写过了,还有一个为没有配置处理函数的路由添加处理程序,默认情况下它返回404代码,下面的代码为没有匹配到路由的请求都返回views/404.html页面。
r.NoRoute(func(c *gin.Context) {c.HTML(http.StatusNotFound, "views/404.html", nil)})
下面我们看看这个路由组吧,有时候这个路由都有这个公共的前缀,但是又有很多个函数此时我们就可以将其抽离出来。下面我们看看这个路由组的写法吧
func main() {r := gin.Default()userGroup := r.Group("/user"){userGroup.GET("/index", func(c *gin.Context) {...})userGroup.GET("/login", func(c *gin.Context) {...})userGroup.POST("/login", func(c *gin.Context) {...})}shopGroup := r.Group("/shop"){shopGroup.GET("/index", func(c *gin.Context) {...})shopGroup.GET("/cart", func(c *gin.Context) {...})shopGroup.POST("/checkout", func(c *gin.Context) {...})}r.Run(":8900")
}
当然这个路由组也支持这个嵌套
shopGroup := r.Group("/shop"){shopGroup.GET("/index", func(c *gin.Context) {...})shopGroup.GET("/cart", func(c *gin.Context) {...})shopGroup.POST("/checkout", func(c *gin.Context) {...})// 嵌套路由组xx := shopGroup.Group("xx")xx.GET("/oo", func(c *gin.Context) {...})}
通常我们将路由分组用在划分业务逻辑或划分API版本时。
Gin框架当中的中间件
Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
Gin中的中间件必须是一个gin.HandlerFunc类型。 这点非常的重要这个是固定不变的
下面我们就写一个小小的Demo来看看这个中间件的写法
package mainimport ("fmt""github.com/gin-gonic/gin""net/http""time"
)//定义一个中间件
func httpfunc(c *gin.Context) {start := time.Now()c.Next() //调用后续的处理函数//c.Abort()//阻止调用后面的函数cost := time.Since(start)fmt.Printf("一共花费了%dms", int(cost))
}
func main() {r := gin.Default()r.Use(httpfunc) //全局注册中间件函数httpfunc,所有的函数都会只会这个全局的中间件函数r.GET("/index", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"msg": "index",})})r.Run(":9098")
}
首先这个客户段的请求进来会执行这个httpFunc函数,这个是安装这个顺序决定的,c.Next()代表的是这个继续执行,而如果我们使用这个c.Abort()那么下面这个函数将不会执行也就是说他的意思就是停止的意思。
Demo2
我们有时候可能会想要记录下某些情况下返回给客户端的响应数据,这个时候就可以编写一个中间件来搞定。
type bodyLogWriter struct {gin.ResponseWriter // 嵌入gin框架ResponseWriterbody *bytes.Buffer // 我们记录用的response
}// Write 写入响应体数据
func (w bodyLogWriter) Write(b []byte) (int, error) {w.body.Write(b) // 我们记录一份return w.ResponseWriter.Write(b) // 真正写入响应
}// ginBodyLogMiddleware 一个记录返回给客户端响应体的中间件
// https://stackoverflow.com/questions/38501325/how-to-log-response-body-in-gin
func ginBodyLogMiddleware(c *gin.Context) {blw := &bodyLogWriter{body: bytes.NewBuffer([]byte{}), ResponseWriter: c.Writer}c.Writer = blw // 使用我们自定义的类型替换默认的c.Next() // 执行业务逻辑fmt.Println("Response body: " + blw.body.String()) // 事后按需记录返回的响应
}
当然这个我们还可以为这个路由组定义这个中间件
shopGroup := r.Group("/shop", StatCost())
{shopGroup.GET("/index", func(c *gin.Context) {...})...
}
当然还有另外一种写法
shopGroup := r.Group("/shop")
shopGroup.Use(StatCost())
{shopGroup.GET("/index", func(c *gin.Context) {...})...
}
Demo3 中间件传递数据
使用Set设置一个key-value,在后续中间件中使用Get接收数据。对应代码如下
package mainimport ("fmt""github.com/gin-gonic/gin"
)type User struct {Name stringAge int
}func m10(c *gin.Context) {fmt.Println("m1 ...in")c.Set("name", User{"ranran", 21})c.Next()fmt.Println("m1 ...out")
}func main() {router := gin.Default()router.Use(m10)router.GET("/", func(c *gin.Context) {fmt.Println("index ...in")name, _ := c.Get("name")user := name.(User)fmt.Println(user.Name, user.Age)c.JSON(200, gin.H{"msg": "index"})})router.Run(":8080")}
value的类型是any类型,所有我们可以用它传任意类型,在接收的时候做好断言即可
中间件的使用还是这个非常好的,当前端发送请求过来之后我们可以定义中间件先检查这个参数的合法性如果不合法我们可以直接Abort停止,如果合法我们可以执行这个配置文件的初始化最后再这个到具体的业务逻辑。
最后说一下这个中间件再使用过程当中的几个注意事项:
1.gin.Default()默认使用了Logger和Recovery中间件,其中:Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release。Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。
如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。
2.gin中间件中使用goroutine
当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())。因为传递指针会修改上一层的内容导致意想不到的事情发生
相关文章:

Go语言Web入门之浅谈Gin框架
Gin框架Gin简介第一个Gin示例HelloworldRESTful APIGin返回数据的几种格式Gin 获取参数HTTP重定向Gin路由&路由组Gin框架当中的中间件Gin简介 Gin 是一个用 Go (Golang) 编写的 web 框架。它是一个类似于 martini 但拥有更好性能的 API 框架,由于 httprouter&a…...

《MySQL学习》 MySQL优化器选择如何选择索引
一.优化器的选择逻辑 建表语句 CREATE TABLE t (id int(11) NOT NULL AUTO_INCREMENT,a int(11) DEFAULT NULL,b int(11) DEFAULT NULL,PRIMARY KEY (id),KEY a (a),KEY b (b) ) ENGINEInnoDB;往表中插入10W条数据 delimiter ;; create procedure idata() begindeclare i in…...

uniapp 悬浮窗(应用内、无需授权) Ba-FloatWindow2
简介(下载地址) Ba-FloatWindow2 是一款应用内并且无需授权的悬浮窗插件。支持多种拖动;自定义位置、大小;支持动态修改。 支持自动定义起始位置支持自定义悬浮窗大小支持贴边显示支持多种拖动方效果:不可拖动、任意…...

MMKV与mmap:全方位解析
概述 MMKV 是基于 mmap 内存映射的移动端通用 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。从 2015 年中至今,在 iOS 微信上使用已有近 3 年,其性能和稳定性经过了时间的验证。近期已移植…...

【信息系统项目管理师】项目管理十大知识领域记忆敲出(整体范围进度)
【信息系统项目管理师】项目管理十大知识领域记忆敲出(整体范围进度) 【信息系统项目管理师】项目管理十大知识领域记忆敲出(整体范围进度)【信息系统项目管理师】项目管理十大知识领域记忆敲出(整体范围进度ÿ…...

一起学 pixijs(3):Sprite
大家好,我是前端西瓜哥。今天来学习 pixijs 的 Sprite。 Sprite pixijs 的 Sprite 类用于将一些纹理(Texture)渲染到屏幕上。 Sprite 直译为 “精灵”,是游戏开发中常见的术语,就是将一个角色的多个动作放到一个图片…...

深入讲解Kubernetes架构-垃圾收集
垃圾收集(Garbage Collection)是 Kubernetes 用于清理集群资源的各种机制的统称。 垃圾收集允许系统清理如下资源:终止的 Pod已完成的 Job不再存在属主引用的对象未使用的容器和容器镜像动态制备的、StorageClass 回收策略为 Delete 的 PV 卷…...

Flink03: 集群安装部署
Flink支持多种安装部署方式 StandaloneON YARNMesos、Kubernetes、AWS… 这些安装方式我们主要讲一下standalone和on yarn。 如果是一个独立环境的话,可能会用到standalone集群模式。 在生产环境下一般还是用on yarn 这种模式比较多,因为这样可以综合利…...

OCR项目实战(一):手写汉语拼音识别(Pytorch版)
✨写在前面:强烈推荐给大家一个优秀的人工智能学习网站,内容包括人工智能基础、机器学习、深度学习神经网络等,详细介绍各部分概念及实战教程,非常适合人工智能领域初学者及研究者学习。➡️点击跳转到网站。 📝OCR专栏…...

【js】export default也在影响项目性能呢
这里写目录标题介绍先说结论分析解决介绍 无意间看到一个关于export与exprot default对比的话题, 于是对二者关于性能方面,有了想法,二者的区别,仅仅是在于写法吗? 于是,有了下面的测试。 先说结论 太长…...

《软件安全》 彭国军 阅读总结
对于本书,小编本意是对其讲述的内容,分点进行笔记的整理,后来学习以后,发现,这本书应该不算是一本技术提升类的书籍,更像是一本领域拓展和知识科普类书籍,所讲知识广泛,但是较少实践…...

深入讲解Kubernetes架构-节点与控制面之间的通信
本文列举控制面节点(确切说是 API 服务器)和 Kubernetes 集群之间的通信路径。 目的是为了让用户能够自定义他们的安装,以实现对网络配置的加固, 使得集群能够在不可信的网络上(或者在一个云服务商完全公开的 IP 上&am…...

120个IT冷知识,看完就不愁做选择题了
目录 IT冷知识 01-10 1.冰淇淋馅料 2.蠕虫起源 3.Linux和红帽子 4."间谍软件"诞生 5.游戏主机的灵魂 6.Linux之父 7.NetBSD的口号 8.安卓起源 9.不是第七代的 Win 7 10.域名金字塔 11~20 11.神奇魔盒 12. 第一个Ubuntu 正式版本 13.巾帼英雄 14.密码…...

Java之动态规划之机器人移动
目录 0.动态规划问题 一.不同路径 1.题目描述 2.问题分析 3.代码实现 二.不同路径 II 1.题目描述 2.问题分析 3.代码实现 三.机器人双向走路 1.题目描述 2.问题分析 3.代码实现 0.动态规划问题 动态规划(Dynamic Programming)算法的核心思想是:将大问题划分为小问…...

seata源码-全局事务提交 服务端源码
前面的博客中,我们介绍了,发起全局事务时,是如何进行全局事务提交的,这篇博客,主要记录,在seata分布式事务中,全局事务提交的时候,服务端是如何进行处理的 发起全局事务提交操作 事…...

C++ 模板
文章目录一、泛型编程二、 函数模板三、类模板一、泛型编程 泛型编程:编写与类型无关的通用代码,代码复用的一种方法 在 C 中,我们可以通过函数重载实现通用的交换函数 Swap ,但是有一些缺点 重载函数只有类型不同,…...

JWT安全漏洞以及常见攻击方式
前言 随着web应用的日渐复杂化,某些场景下,仅使用Cookie、Session等常见的身份鉴别方式无法满足业务的需要,JWT也就应运而生,JWT可以有效的解决分布式场景下的身份鉴别问题,并且会规避掉一些安全问题,如CO…...

华为OD机试题 - 最小施肥机能效(JavaScript)
最近更新的博客 华为OD机试题 - 任务总执行时长(JavaScript) 华为OD机试题 - 开放日活动(JavaScript) 华为OD机试 - 最近的点 | 备考思路,刷题要点,答疑 【新解法】 华为OD机试题 - 最小步骤数(JavaScript) 华为OD机试题 - 任务混部(JavaScript) 华为OD机试题 - N 进…...

Python(1)变量的命名规则
目录 1.变量的命名原则 3.内置函数尽量不要做变量 4.删除变量和垃圾回收机制 5.结语 参考资料 1.变量的命名原则 ①由英文字母、_(下划线)、或中文开头 ②变量名称只能由英文字母、数字、下画线或中文字所组成。 ③英文字母大小写不相同 实例: 爱_aiA1 print(…...

Shiro1.9学习笔记
文章目录一、Shiro概述1、Shiro简介1.1 介绍1.2 Shiro特点2、Shiro与SpringSecurity的对比3、Shiro基本功能4、Shiro原理4.1 Shiro 架构(外部)4.2 shiro架构(内部)二、Shiro基本使用1、环境准备2、登录认证2.1 登录认证概念2.2 登录认证基本流程2.3 登录认证实例2.4 身份认证源…...
2.5|iot|嵌入式Linux系统开发与应用|第4章:Linux外壳shell脚本程序编程
1.shell基础 Shell是Linux操作系统内核的外壳,它为用户提供使用操作系统的命令接口。 用户在提示符下输入的每个命令都由shell先解释然后发给Linux内核,所以Linux中的命令通称为shell命令。 通常我们使用shell来使用Linux操作系统。Linux系统的shell是…...

九龙证券|连续七周获加仓,四大行业成“香饽饽”!
本周17个申万职业北上资金持股量环比增加。 北上资金抢筹铝业龙头 本周A股商场全体冲高回落,沪指收跌1.12%,深成指跌2.18%,创业板指跌3.76%。北上资金周内小幅净流入。在大盘体现较差的周四周五,北上资金别离逆市回流67.94亿元、…...

210天从外包踏进华为跳动那一刻,我泪目了
前言 没有绝对的天才,只有持续不断的付出。对于我们每一个平凡人来说,改变命运只能依靠努力幸运,但如果你不够幸运,那就只能拉高努力的占比。 2021年4月,我有幸成为了华为的一名高级测试工程师,正如标题所…...

CMake 引入第三方库
CMake 引入第三方库 在 CMake 中,如何引入第三方库是一个常见的问题。在本文中,我们将介绍 CMake 中引入第三方库的不同方法,以及它们的优缺点。 1. 使用 find_package 命令 在 CMake 中,使用 find_package 命令是最简单和最常…...

软考中级-面向对象
面向对象基础(1)类类分为三种:实体类(世间万物)、接口类(又称边界类,提供用户与系统交互的方式)、控制类(前两类之间的媒介)。对象:由对象名数据&…...

Linux 系统构成:bootloader、kernel、rootfs
写在前面: 本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。 目录前言bootloaderk…...

SpringCloud - Eureka注册发现
目录 提供者与消费者 Eureka原理分析 搭建Eureka服务 服务注册 服务发现 提供者与消费者 服务提供者: 一次业务中,被其它微服务调用的服务(提供接口给其它微服务)服务消费者: 一次业务中,调用其它微服务的服务(调用其它微服务…...

WampServer安装教程
文章目录简介:官网地址安装步骤:我是阿波,学习PHP记录一下笔记,如果对你有帮助,欢迎一键三连,谢谢! 简介: WampServer是一个用于Windows操作系统的Web开发环境,其名称来…...

Go语言泛型基础
泛型 Go 并不是一种静止的、一成不变的编程语言。新的功能是在经过大量的讨论和实验后慢慢采用的。最初的 Go1.0发布以来,Go语言习惯的模式已经发生了重大变化1.7的context、1.11的modules、1.13 error嵌套等Go的 1.18 版本包括了类型参数的实现,也就是…...

基于android的中医养生app
需求信息: 中医健康养生APP分为四大模块,其中个人中心又分为4大块,游客用户个人中心是空白的。 上图为养生知识推广普及模块的功能结构图。 在养生知识推广普及模块界面,用户可以选择自己感兴趣的模块进行文章浏览,文章…...