【Golang】gin框架入门

文章目录

  • gin框架入门
    • 认识gin
      • go流行的web框架
      • gin介绍
      • 快速入门
    • 路由
      • RESTful API规范
      • 请求方法
      • URI
      • 处理函数
      • 分组路由
    • 请求参数
      • GET请求参数
      • POST请求参数
      • 路径参数
      • 文件参数
    • 响应
      • 字符串方式
      • JSON方式
      • XML方式
      • 文件格式
      • 设置HTTP响应头
      • 重定向
      • YAML方式
    • 模板渲染
      • 基本使用
      • 多个模板渲染
      • 自定义模板函数
      • 静态文件处理
    • 会话
    • 中间件
      • 中间件使用
      • 自定义中间件

gin框架入门

认识gin

go流行的web框架

  • Gin

    号称最快的go语言web框架,目前是go官方的推荐框架(https://go.dev/doc/tutorial/)

  • iris

    性能比gin高一些,支持MVC,但这款框架评价不太好,使用上问题较多,近些年很少去选择使用

  • Beego

    国人开发,最早的go web框架之一,工具集比较完善,性能较差,据传言作者是php转行,所以框架带有浓厚的php特色,早期国内使用的多,目前少有人选择

  • fiber

    2020年发布的框架,发展迅速,建立在fasthttp之上,性能目前最高,受Express启发,比较简洁,上手较快,和gin类似

当然还有其他一些框架,但从star数上,以及流行程度上看,gin一骑绝尘,gin的好处在于其简洁,扩展性,稳定性以及性能都比较出色。

go的框架其实是可以理解为库,并不是用了某一个框架就不能用别的框架,可以选择性的使用各个库中的优秀组件,进行组合

gin介绍

特性:

  • 快速

    基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能

  • 支持中间件

    传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作 DB

  • Crash处理

    Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。例如,你可以向 Sentry 报告这个 panic!

  • JSON验证

    Gin 可以解析并验证请求的 JSON,例如检查所需值的存在

  • 路由组

    更好地组织路由。是否需要授权,不同的 API 版本…… 此外,这些组可以无限制地嵌套而不会降低性能

  • 错误管理

    Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送

  • 内置渲染

    Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API

  • 可扩展性

    新建一个中间件非常简单

快速入门

go版本需求:go1.13及以上

环境:windows 11

  • gin框架下载

    go get -u github.com/gin-gonic/gin

    在这里插入图片描述

  • demo展示

    package mainimport "github.com/gin-gonic/gin"func main() {r := gin.Default()r.GET("/ping", func(c *gin.Context) {c.JSON(200, gin.H{"message": "pong",})})r.Run() // 监听并在 0.0.0.0:8080 上启动服务
    }
    

    在这里插入图片描述

    此时,通过postman发送请求测试是否联通

    在这里插入图片描述

路由

路由是URI到函数的映射。

一个URI含: http://localhost:8080/user/find?id=11

  • 协议,比如http,https等
  • ip端口或者域名,比如127.0.0.1:8080或者www.test.com
  • path,比如 /path
  • query,比如 ?query

同时访问的时候,还需要指明HTTP METHOD,比如

  • GET

    GET方法请求一个指定资源的表示形式. 使用GET的请求应该只被用于获取数据.

  • POST

    POST方法用于将实体提交到指定的资源,通常会导致在服务器上的状态变化

  • HEAD

    HEAD方法请求一个与GET请求的响应相同的响应,但没有响应体.

  • PUT

    PUT方法用请求有效载荷替换目标资源的所有当前表示

  • DELETE

    DELETE方法删除指定的资源

  • CONNECT

    CONNECT方法建立一个到由目标资源标识的服务器的隧道。

  • OPTIONS

    OPTIONS方法用于描述目标资源的通信选项。

  • TRACE

    TRACE方法沿着到目标资源的路径执行一个消息环回测试。

  • PATCH

    PATCH方法用于对资源应用部分修改。

使用的时候,应该尽量遵循其语义

RESTful API规范

RESTful API 的规范建议我们使用特定的HTTP方法来对服务器上的资源进行操作。

比如:

  1. GET,表示读取服务器上的资源
  2. POST,表示在服务器上创建资源
  3. PUT,表示更新或者替换服务器上的资源
  4. DELETE,表示删除服务器上的资源
  5. PATCH,表示更新/修改资源的一部分

请求方法

比如

	r.GET("/get", func(ctx *gin.Context) {ctx.JSON(200, "get")})r.POST("/post", func(ctx *gin.Context) {ctx.JSON(200, "post")})r.DELETE("/delete", func(ctx *gin.Context) {ctx.JSON(200, "delete")})r.PUT("/put", func(ctx *gin.Context) {ctx.JSON(200, "put")})

如果想要支持所有:

r.Any("/any", func(ctx *gin.Context) {ctx.JSON(200, "any")})

如果想要支持其中的几种:

   r.GET("/hello", func(ctx *gin.Context) {//数组 map list 结构体ctx.JSON(200, gin.H{"name": "hello world",})})r.POST("/hello", func(ctx *gin.Context) {//数组 map list 结构体ctx.JSON(200, gin.H{"name": "hello world",})})

URI

URI书写的时候,我们不需要关心scheme和authority这两部分,我们主要通过path和query两部分的书写来进行资源的定位

  • 静态url,比如/hello/user/find

    r.POST("/user/find", func(ctx *gin.Context) {
    })
    
  • 路径参数,比如/user/find/:id

    r.POST("/user/find/:id", func(ctx *gin.Context) {param := ctx.Param("id")ctx.JSON(200, param)})
    
  • 模糊匹配,比如/user/*path

    r.POST("/user/*path", func(ctx *gin.Context) {param := ctx.Param("path")ctx.JSON(200, param)
    })
    

处理函数

定义:

type HandlerFunc func(*Context)

通过上下文的参数,获取http的请求参数,响应http请求等。

分组路由

在进行开发的时候,我们往往要进行模块的划分,比如用户模块,以user开发,商品模块,以goods开头。

或者进行多版本开发,不同版本之间路径是一致的,这种时候,就可以用到分组路由了。

比如:

  ug := r.Group("/user"){ug.GET("find", func(ctx *gin.Context) {ctx.JSON(200, "user find")})ug.POST("save", func(ctx *gin.Context) {ctx.JSON(200, "user save")})}gg := r.Group("/goods"){gg.GET("find", func(ctx *gin.Context) {ctx.JSON(200, "goods find")})gg.POST("save", func(ctx *gin.Context) {ctx.JSON(200, "goods save")})}

请求参数

GET请求参数

使用Get请求传参时,类似于这样 http://localhost:8080/user/save?id=11&name=zhangsan

如何获取呢?

  • 1.1 普通参数

    request url: http://localhost:8080/user/save?id=11&name=zhangsan

    r.GET("/user/save", func(ctx *gin.Context) {id := ctx.Query("id")name := ctx.Query("name")ctx.JSON(200, gin.H{"id":   id,"name": name,})})
    

    如果参数不存在,就会给一个默认值

    r.GET("/user/save", func(ctx *gin.Context) {id := ctx.Query("id")name := ctx.Query("name")address := ctx.DefaultQuery("address", "北京")ctx.JSON(200, gin.H{"id":      id,"name":    name,"address": address,})})
    

    因此,为了正常处理业务,我们需要判断参数是否存在

    r.GET("/user/save", func(ctx *gin.Context) {id, ok := ctx.GetQuery("id")address, aok := ctx.GetQuery("address")ctx.JSON(200, gin.H{"id":      id,"idok":    ok,"address": address,"aok":     aok,})})
    

    id是数值类型,上述获取的都是string类型,根据类型获取方式:

    type User struct {Id   int64  `form:"id"`Name string `form:"name"`
    }
    r.GET("/user/save", func(ctx *gin.Context) {var user Usererr := ctx.BindQuery(&user)if err != nil {log.Println(err)}ctx.JSON(200, user)
    })
    

    也可以通过这个方式:

    r.GET("/user/save", func(ctx *gin.Context) {var user Usererr := ctx.ShouldBindQuery(&user)if err != nil {log.Println(err)}ctx.JSON(200, user)})
    
  • 参数是数组

    请求url:http://localhost:8080/user/save?address=Beijing&address=shanghai

    r.GET("/user/save", func(ctx *gin.Context) {address := ctx.QueryArray("address")ctx.JSON(200, address)})
    
    r.GET("/user/save", func(ctx *gin.Context) {address, ok := ctx.GetQueryArray("address")fmt.Println(ok)ctx.JSON(200, address)})
    
    r.GET("/user/save", func(ctx *gin.Context) {var user Usererr := ctx.ShouldBindQuery(&user)fmt.Println(err)ctx.JSON(200, user)})
    
  • map参数

    请求url:http://localhost:8080/user/save?addressMap[home]=Beijing&addressMap[company]=shanghai

    r.GET("/user/save", func(ctx *gin.Context) {addressMap := ctx.QueryMap("addressMap")ctx.JSON(200, addressMap)})
    
    r.GET("/user/save", func(ctx *gin.Context) {addressMap, _ := ctx.GetQueryMap("addressMap")ctx.JSON(200, addressMap)})
    

    map参数 bind并没有支持

POST请求参数

post请求一般是表单参数和json参数

  • 表单参数

    r.POST("/user/save", func(ctx *gin.Context) {id := ctx.PostForm("id")name := ctx.PostForm("name")address := ctx.PostFormArray("address")addressMap := ctx.PostFormMap("addressMap")ctx.JSON(200, gin.H{"id":         id,"name":       name,"address":    address,"addressMap": addressMap,})})
    
    r.POST("/user/save", func(ctx *gin.Context) {var user Usererr := ctx.ShouldBind(&user)addressMap, _ := ctx.GetPostFormMap("addressMap")user.AddressMap = addressMapfmt.Println(err)ctx.JSON(200, user)})
    
  • JSON参数

    {"id":1111,"name":"zhangsan","address": ["beijing","shanghai"],"addressMap":{"home":"beijing"}
    }
    
    r.POST("/user/save", func(ctx *gin.Context) {var user Usererr := ctx.ShouldBindJSON(&user)fmt.Println(err)ctx.JSON(200, user)})
    

路径参数

请求url:http://localhost:8080/user/save/111

r.POST("/user/save/:id", func(ctx *gin.Context) {ctx.JSON(200, ctx.Param("id"))})

文件参数

r.POST("/user/save", func(ctx *gin.Context) {form, err := ctx.MultipartForm()if err != nil {log.Println(err)}files := form.Filefor _, fileArray := range files {for _, v := range fileArray {ctx.SaveUploadedFile(v, "./"+v.Filename)}}ctx.JSON(200, form.Value)})

响应

字符串方式

r.GET("/user/save", func(ctx *gin.Context) {ctx.String(http.StatusOK, "this is a %s", "ms string response")})

JSON方式

r.GET("/user/save", func(ctx *gin.Context) {ctx.JSON(http.StatusOK, gin.H{"success": true,})})

XML方式

type XmlUser struct {Id   int64  `xml:"id"`Name string `xml:"name"`
}
r.GET("/user/save", func(ctx *gin.Context) {u := XmlUser{Id:   11,Name: "zhangsan",}ctx.XML(http.StatusOK, u)})

文件格式

r.GET("/user/save", func(ctx *gin.Context) {//ctx.File("./1.png")ctx.FileAttachment("./1.png", "2.png")})

设置HTTP响应头

r.GET("/user/save", func(ctx *gin.Context) {ctx.Header("test", "headertest")})

重定向

r.GET("/user/save", func(ctx *gin.Context) {ctx.Redirect(http.StatusMovedPermanently, "http://www.baidu.com")})

YAML方式

r.GET("/user/save", func(ctx *gin.Context) {ctx.YAML(200, gin.H{"name": "ms", "age": 19})
})

模板渲染

模板是golang语言的一个标准库,使用场景很多,gin框架同样支持模板

基本使用

定义一个存放模板文件的templates文件夹

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>gin_templates</title>
</head>
<body>
{{.title}}
</body>
</html>

后端代码:

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()// 模板解析r.LoadHTMLFiles("templates/index.tmpl")r.GET("/index", func(c *gin.Context) {// HTML请求// 模板的渲染c.HTML(http.StatusOK, "index.tmpl", gin.H{"title": "hello 模板",})})r.Run(":9090") // 启动server
}

多个模板渲染

如果有多个模板,可以统一进行渲染

// 模板解析,解析templates目录下的所有模板文件r.LoadHTMLGlob("templates/**")

如果目录为templates/post/index.tmpltemplates/user/index.tmpl这种,可以

	// **/* 代表所有子目录下的所有文件router.LoadHTMLGlob("templates/**/*")

自定义模板函数

   // gin框架给模板添加自定义函数r.SetFuncMap(template.FuncMap{"safe": func(str string) template.HTML {return template.HTML(str)},})// 模板解析,解析templates目录下的所有模板文件r.LoadHTMLGlob("templates/**")r.GET("/index", func(c *gin.Context) {// HTML请求// 模板的渲染c.HTML(http.StatusOK, "index.tmpl", gin.H{"title": "<a href='http://baidu.com'>跳转到其他地方</a>",})})
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>gin_templates</title>
</head>
<body>
{{.title | safe}}
</body>
</html>Z

静态文件处理

如果在模板中引入静态文件,比如样式文件

index.css

body{background-color: aqua;
}
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>gin_templates</title><link rel="stylesheet" href="/css/index.css">
</head>
<body>
{{.title}}
</body>
</html>
// 加载静态文件
r.Static("/css", "./static/css")

会话

会话控制涉及到cookie和session的使用

  • cookie

    1. HTTP是无状态协议,服务器不能记录浏览器的访问状态,也就是说服务器不能区分两次请求是否由同一个客户端发出
    2. Cookie就是解决HTTP协议无状态的方案之一
    3. Cookie实际上就是服务器保存在浏览器上的一段信息。浏览器有了Cookie之后,每次向服务器发送请求时都会同时将该信息发送给服务器,服务器收到请求后,就可以根据该信息处理请求
    4. Cookie由服务器创建,并发送给浏览器,最终由浏览器保存

    设置cookie

    func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)
    

    参数说明:

    参数名类型说明
    namestringcookie名字
    valuestringcookie值
    maxAgeint有效时间,单位是秒,MaxAge=0 忽略MaxAge属性,MaxAge<0 相当于删除cookie, 通常可以设置-1代表删除,MaxAge>0 多少秒后cookie失效
    pathstringcookie路径
    domainstringcookie作用域
    secureboolSecure=true,那么这个cookie只能用https协议发送给服务器
    httpOnlybool设置HttpOnly=true的cookie不能被js获取到
    r.GET("/cookie", func(c *gin.Context) {// 设置cookiec.SetCookie("site_cookie", "cookievalue", 3600, "/", "localhost", false, true)})
    

    获取cookie

    r.GET("/read", func(c *gin.Context) {// 根据cookie名字读取cookie值data, err := c.Cookie("site_cookie")if err != nil {// 直接返回cookie值c.String(200,data)return}c.String(200,"not found!")})
    

    删除cookie

    通过将cookie的MaxAge设置为-1, 达到删除cookie的目的。

    r.GET("/del", func(c *gin.Context) {// 设置cookie  MaxAge设置为-1,表示删除cookiec.SetCookie("site_cookie", "cookievalue", -1, "/", "localhost", false, true)c.String(200,"删除cookie")})
    
  • session

    在Gin框架中,我们可以依赖gin-contrib/sessionsopen in new window中间件处理session

    安装session包

    go get github.com/gin-contrib/sessions
    
    package mainimport ("fmt""github.com/gin-contrib/sessions""github.com/gin-contrib/sessions/cookie""github.com/gin-gonic/gin"
    )func main() {r := gin.Default()// 创建基于cookie的存储引擎,secret 参数是用于加密的密钥store := cookie.NewStore([]byte("secret"))// 设置session中间件,参数mysession,指的是session的名字,也是cookie的名字// store是前面创建的存储引擎,我们可以替换成其他存储引擎r.Use(sessions.Sessions("mysession", store))r.GET("/hello", func(c *gin.Context) {// 初始化session对象session := sessions.Default(c)// 通过session.Get读取session值// session是键值对格式数据,因此需要通过key查询数据if session.Get("hello") != "world" {fmt.Println("没读到")// 设置session数据session.Set("hello", "world")session.Save()}c.JSON(200, gin.H{"hello": session.Get("hello")})})r.Run(":8080")
    }
    

    多session

    package mainimport ("github.com/gin-contrib/sessions""github.com/gin-contrib/sessions/cookie""github.com/gin-gonic/gin"
    )func main() {r := gin.Default()store := cookie.NewStore([]byte("secret"))sessionNames := []string{"a", "b"}r.Use(sessions.SessionsMany(sessionNames, store))r.GET("/hello", func(c *gin.Context) {sessionA := sessions.DefaultMany(c, "a")sessionB := sessions.DefaultMany(c, "b")if sessionA.Get("hello") != "world!" {sessionA.Set("hello", "world!")sessionA.Save()}if sessionB.Get("hello") != "world?" {sessionB.Set("hello", "world?")sessionB.Save()}c.JSON(200, gin.H{"a": sessionA.Get("hello"),"b": sessionB.Get("hello"),})})r.Run(":8080")
    }
    

中间件

在Gin框架中,中间件(Middleware)指的是可以拦截http请求-响应生命周期的特殊函数,在请求-响应生命周期中可以注册多个中间件,每个中间件执行不同的功能,一个中间执行完再轮到下一个中间件执行

中间件的常见应用场景如下:

  • 请求限速
  • api接口签名处理
  • 权限校验
  • 统一错误处理

Gin支持设置全局中间件和针对路由分组设置中间件,设置全局中间件意思就是会拦截所有请求,针对分组路由设置中间件,意思就是仅对这个分组下的路由起作用

中间件使用

 r := gin.New()// 通过use设置全局中间件// 设置日志中间件,主要用于打印请求日志r.Use(gin.Logger())// 设置Recovery中间件,主要用于拦截paic错误,不至于导致进程崩掉r.Use(gin.Recovery())r.GET("/test", func(ctx *gin.Context) {panic(errors.New("test error"))})r.Run(":8080")

自定义中间件

使用Use可以使用gin自带的中间件或者其他第三方中间件,也可以自己开发中间件

package main
// 导入gin包
import (
"github.com/gin-gonic/gin""log""time"
)// 自定义个日志中间件
func Logger() gin.HandlerFunc {return func(c *gin.Context) {t := time.Now()// 可以通过上下文对象,设置一些依附在上下文对象里面的键/值数据c.Set("example", "12345")// 在这里处理请求到达控制器函数之前的逻辑// 调用下一个中间件,或者控制器处理函数,具体得看注册了多少个中间件。c.Next()// 在这里可以处理请求返回给用户之前的逻辑latency := time.Since(t)log.Print(latency)// 例如,查询请求状态吗status := c.Writer.Status()log.Println(status)}
}func main() {r := gin.New()// 注册上面自定义的日志中间件r.Use(Logger())r.GET("/test", func(c *gin.Context) {// 查询我们之前在日志中间件,注入的键值数据example := c.MustGet("example").(string)// it would print: "12345"log.Println(example)})// Listen and serve on 0.0.0.0:8080r.Run(":8080")
}
	panic(errors.New("test error"))
})
r.Run(":8080")

### 自定义中间件使用Use可以使用gin自带的中间件或者其他第三方中间件,也可以自己开发中间件```go
package main
// 导入gin包
import (
"github.com/gin-gonic/gin""log""time"
)// 自定义个日志中间件
func Logger() gin.HandlerFunc {return func(c *gin.Context) {t := time.Now()// 可以通过上下文对象,设置一些依附在上下文对象里面的键/值数据c.Set("example", "12345")// 在这里处理请求到达控制器函数之前的逻辑// 调用下一个中间件,或者控制器处理函数,具体得看注册了多少个中间件。c.Next()// 在这里可以处理请求返回给用户之前的逻辑latency := time.Since(t)log.Print(latency)// 例如,查询请求状态吗status := c.Writer.Status()log.Println(status)}
}func main() {r := gin.New()// 注册上面自定义的日志中间件r.Use(Logger())r.GET("/test", func(c *gin.Context) {// 查询我们之前在日志中间件,注入的键值数据example := c.MustGet("example").(string)// it would print: "12345"log.Println(example)})// Listen and serve on 0.0.0.0:8080r.Run(":8080")
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/154230.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt中QTimer定时器的用法

Qt中提供了两种定时器的方式一种是使用Qt中的事件处理函数&#xff0c;另一种就是Qt中的定时器类QTimer。 使用QTimer类&#xff0c;需要创建一个QTimer类对象&#xff0c;然后调用其start()方法开启定时器&#xff0c;此后QTimer对象就会周期性的发出timeout()信号。 1.QTimer…

VMware centos7虚拟机修改静态IP

一、修改网络适配器 1、打开 2、使用管理员权限修改 3、按照图中步骤修改为 4、设置网关为10.0.0.2后保存即可 二、修改配置文件 1、输入下面代码进入修改&#xff08;网卡这里网卡名字为ens33&#xff0c;可使用ifcfig或ip a查看&#xff09; vi /etc/sysconfig/netwo…

使用vlc获取海康威视视频流

1.下载相关软件 1.1海康威视官网-服务支持-工具软件-设备网络搜索 下载地址&#xff1a; https://www.hikvision.com/cn/support/tools/hitools/注意&#xff1a;必须跟摄像头在同一个局域网下才可以使用设备网络搜索工具&#xff0c;才能使用vlc获取到视频流。 1.2下载VLC …

Hadoop----Azkaban的使用与一些报错问题的解决

1.因为官方只放出源码&#xff0c;并没有放出其tar包&#xff0c;所以需要我们自己编译&#xff0c;通过查阅资料我们可以使用gradlew对其进行编译&#xff0c;还是比较简单&#xff0c;然后将里面需要用到的服务文件夹进行拷贝&#xff0c;完善其文件夹结构&#xff0c;通常会…

leetcode 每日一题复盘(10.9~10.15)

leetcode 101 对称二叉树 这道题一开始想是用层序遍历,看每一层是否都对称,遇到一个问题就是空指针(子树为空)无法记录下来,同时会导致操作空指针的问题,因此需要修改入队条件,并用一个标志去表示空指针 vector<int>numv;for(int i0;i<size;i){TreeNode*frontque.fro…

SpringCloudGateway网关整合swagger3+Knife4j3,basePath丢失请求404问题

很多人都是照着别人的文章粘代码&#xff0c;我也是粘的&#xff0c;但是这样粘也会有问题&#xff0c;我搞这个Knife4j3的时候遇到两个问题&#xff0c;这里记录一下&#xff1a; 第一个是basePath丢失&#xff0c;第二个解决basePath丢失完又引发了会引起application/json数据…

React +ts + babel+webpack

babel babel/preset-typescript 专门处理ts "babel/cli": "^7.17.6", "babel/core": "^7.17.8", "babel/preset-env": "^7.16.11", "babel/preset-react": "^7.16.7", "babel/preset…

第4章 决策树

文章目录 4.1 基本流程4.2 划分选择4.2.1 信息增益4.2.2 增益率4.2.3 基尼指数 4.3 剪枝处理4.3.1 预剪枝4.3.2 后剪枝 4.4 连续与缺失值4.4.1 连续值处理4.4.2 缺失值处理 4.5 多变量决策树4.6 阅读材料 4.1 基本流程 决策树也称判定树&#xff0c;是一类常见的机器学习方法。…

35 WEB漏洞-逻辑越权之找回机制及接口安全

目录 找回重置机制接口调用乱用演示案例绑定手机验证码逻辑-Rep状态值篡改-实例某APP短信轰炸接口乱用-实例接口调用发包 文章分享&#xff1a;https://www.cnblogs.com/zhengna/p/15655691.html 有支付接口、短信发送接口&#xff0c;邮箱的发送接口等等&#xff0c;在接口这…

论文研读|Protecting Intellectual Property of Deep Neural Networks with Watermarking

目录 论文信息文章简介研究动机研究方法水印生成水印嵌入版权验证 实验结果有效性&#xff08;Effectiveness&#xff09;高效性&#xff08;Converge Speed&#xff09;保真度&#xff08;Functionality&#xff09;鲁棒性&#xff08;Robustness&#xff09;Anti-剪枝攻击&am…

镜像仓库harbor安装部署

基础配置 systemctl stop firewalld && systemctl disable firewalld setenforce 0 sed -i s/SELINUXenforcing/SELINUXdisabled/ /etc/selinux/configharbor wget http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum install -y docker-ce docke…

Python中套接字实现服务端和客户端3-2

2.3 监听套接字 通过listen()方法监听套接字。该方法的格式如下所示。 socket.listen([backlog]) 其中&#xff0c;参数backlog是一个可选项&#xff0c;表示等待服务器接收连接的客户端的数量。使用listen()方法监听套接字的代码如下所示。 s.listen(1) 当没有客户端连接…

OJ练习第183题——移动机器人

移动机器人 力扣链接&#xff1a;2731. 移动机器人 题目描述 示例 官解思路 当两个机器人相撞时&#xff0c;它们会沿着原本相反的方向移动。由于机器人之间并没有任何区别&#xff0c;相撞可以看做是穿透&#xff0c;原本左边的机器人相撞后交换为右边的机器人&#xff0c…

android Google官网 :支持不同的语言和文化 rtl / ltr : 本地化适配:RTL(right-to-left) 适配

参考 google官网&#xff1a; 支持不同的语言和文化 应用包含可能专门针对特定文化而设计的资源。例如&#xff0c;应用可以包含针对特定文化的字符串&#xff0c;这些字符串将转换为当前语言区域的语言。 将具有文化特异性的资源与应用的其他资源分开是一种很好的做法。And…

HTTPS建立连接的过程

HTTPS 协议是基于 TCP 协议的&#xff0c;因而要先建立 TCP 的连接。在这个例子中&#xff0c;TCP 的连接是在手机上的 App 和负载均衡器 SLB 之间的。 尽管中间要经过很多的路由器和交换机&#xff0c;但是 TCP 的连接是端到端的。TCP 这一层和更上层的 HTTPS 无法看到中间的包…

基于springboot实现家具销售电商平台管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现家具销售电商平台管理系统演示 摘要 社会的发展和科学技术的进步&#xff0c;互联网技术越来越受欢迎。网络计算机的交易方式逐渐受到广大人民群众的喜爱&#xff0c;也逐渐进入了每个用户的使用。互联网具有便利性&#xff0c;速度快&#xff0c;效率高&am…

NLP 项目:维基百科文章爬虫和分类【01】 - 语料库阅读器

自然语言处理是机器学习和人工智能的一个迷人领域。这篇博客文章启动了一个具体的 NLP 项目&#xff0c;涉及使用维基百科文章进行聚类、分类和知识提取。灵感和一般方法源自《Applied Text Analysis with Python》一书。 一、说明 该文是系列文章&#xff0c;揭示如何对爬取文…

SQL多表设计--一对多(外键)

-- 完成部门和员工的-- 选择当前db03 这个数据库use db03;-- 查看当前选中的数据库select database();-- 创建员工表create table tb_emp (id int unsigned primary key auto_increment comment ID,username varchar(20) not null unique comment 用户名,password varchar(32)…

多线程(线程互斥)

抢票代码编写 学习了前面有关线程库的操作后&#xff0c;我们就可以模拟抢票的过程 假设我们创建四个线程&#xff0c;分别代表我们的用户 然后设定总票数为1000张&#xff0c;四个线程分别将进行循环抢票操作&#xff0c;其实就是循环对票数进行打印&#xff0c;并进行对应的…

粘性文本整页滚动效果

效果展示 CSS 知识点 background 相关属性综合运用position 属性的 sticky 值运用scroll-behavior 属性运用scroll-snap-type 属性运用scroll-snap-align 属性运用 整体页面效果实现 <div class"container"><!-- 第一屏 --><div class"sec&qu…