当前位置: 首页 > news >正文

app开发网站排行做网站图片切图是什么

app开发网站排行,做网站图片切图是什么,网站主机免费申请,世界卫生健康论坛文章目录 gin框架路由详解#xff08;1#xff09;go mod tidy#xff08;2#xff09;r : gin.Default()#xff08;3#xff09;r.GET()路由注册 #xff08;4#xff09;r.Run()路由匹配 总结 gin框架路由详解 先创建一个项目#xff0c;编写一个简单的demo#… 文章目录 gin框架路由详解1go mod tidy2r : gin.Default()3r.GET()路由注册 4r.Run()路由匹配 总结 gin框架路由详解 先创建一个项目编写一个简单的demo对这个demo进行讲解。 创建一个go的项目采用GoLand 2. 在该项目下创建一个main.go文件 package mainimport (net/httpgithub.com/gin-gonic/gin )func main() {r : gin.Default()r.GET(/, func(c *gin.Context) {c.String(http.StatusOK, hello word)})r.Run(:8000) }上面就是一个非常简单的gin的使用逐行代码解读在解读前需要先下载gin库写入main.go后在terminal执行go mod tidy命令就会添加main里面的gin库。 1go mod tidy 添加缺失的依赖 如果你的代码中引用了某些依赖通过 import但它们没有被记录在 go.mod 文件中go mod tidy 会自动将这些缺失的依赖添加到 go.mod 文件中。 移除未使用的依赖 如果你的代码中不再使用某些依赖即没有通过 import 引用go mod tidy 会从 go.mod 文件中移除这些无用的依赖。 更新 go.sum 文件 go mod tidy 会检查 go.sum 文件存储模块的校验和是否与 go.mod 文件一致 ​ -如果某些依赖的校验和缺失它会添加。 ​ -如果某些校验和多余对应的依赖已被移除它会删除。 2r : gin.Default() 创建默认的Gin引擎点进这个方法查看源码 // Default returns an Engine instance with the Logger and Recovery middleware already attached. func Default(opts ...OptionFunc) *Engine {debugPrintWARNINGDefault()engine : New()engine.Use(Logger(), Recovery())return engine.With(opts...) }返回了Engine的指针结构体还包括一些日志和中断复原的操作。 关于Engine结构体【是 Gin 框架的核心结构体它既是路由表的管理器也是 HTTP 服务的入口】 type Engine struct {RouterGrouptrees methodTrees // 每种 HTTP 方法对应的路由树maxParams uint16 // 路由参数最大数量maxSections uint16 // 路由路径最大分段数量handlers404 HandlersChain // 404 处理函数链// 其他字段... }trees: 存储路由表的核心字段每种 HTTP 方法有一棵对应的 Radix 树。 RouterGroup: 用于管理路由组和中间件。 handlers404: 默认的 404 错误处理。 关于trees是路由规则的核心存储路由表每种 HTTP 方法有一棵对应的 Radix 树。 1Radix 树 公共前缀的树结构是一种更节省空间的前缀树Trie Tree。对于基数树的每个节点如果该节点是唯一的子树的话就和父节点合并。下图为一个基数树示例 2methodTrees type methodTree struct {method stringroot *node }type methodTrees []methodTreemethod是http的类型每个路由路径的片段都由一个node节点构成 type node struct {path string // 当前节点的路径部分indices string // 子节点的索引用于快速查找children []*node // 子节点handlers HandlersChain // 当前节点的处理函数priority uint32 // 优先级用于优化匹配顺序wildChild bool // 是否包含通配符子节点nType nodeType // 节点类型: static, param, catchAll } path: 存储路径片段。 indices: 子节点索引表示每个子节点的第一个字符用于快速查找。 handlers: 当前节点绑定的处理函数。 wildChild: 是否有动态或通配符子节点。 nType: static: 静态路径节点。param: 动态路径节点如 :id。catchAll: 通配符节点如 *filepath。 3r.GET() Gin 的路由实现主要分为路由注册和路由匹配两部分。 路由注册 点进GET方法 // GET is a shortcut for router.Handle(GET, path, handlers). func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {return group.handle(http.MethodGet, relativePath, handlers) }handle方法 func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {absolutePath : group.calculateAbsolutePath(relativePath)handlers group.combineHandlers(handlers)group.engine.addRoute(httpMethod, absolutePath, handlers)return group.returnObj() }addRoute方法【注册路由的核心函数】 func (e *Engine) addRoute(method, path string, handlers HandlersChain) {root : e.trees.get(method) // 获取当前方法对应的路由树if root nil {root new(node) // 如果路由树不存在创建新的树e.trees append(e.trees, methodTree{method: method, root: root})}root.addRoute(path, handlers) // 将路径插入到 Radix 树中 } Radix 树节点插入逻辑addRoute in node 在 node 中的 addRoute 方法负责将路径拆分并插入到树中 // addRoute 将具有给定句柄的节点添加到路径中。 // 不是并发安全的 func (n *node) addRoute(path string, handlers HandlersChain) {fullPath : pathn.prioritynumParams : countParams(path) // 数一下参数个数// 空树就直接插入当前节点if len(n.path) 0 len(n.children) 0 {n.insertChild(numParams, path, fullPath, handlers)n.nType rootreturn}parentFullPathIndex : 0walk:for {// 更新当前节点的最大参数个数if numParams n.maxParams {n.maxParams numParams}// 找到最长的通用前缀// 这也意味着公共前缀不包含“:”或“*” /// 因为现有键不能包含这些字符。i : longestCommonPrefix(path, n.path)// 分裂边缘此处分裂的是当前树节点// 例如一开始path是search新加入supports是他们通用的最长前缀部分// 那么会将s拿出来作为parent节点增加earch和upport作为child节点if i len(n.path) {child : node{path: n.path[i:], // 公共前缀后的部分作为子节点wildChild: n.wildChild,indices: n.indices,children: n.children,handlers: n.handlers,priority: n.priority - 1, //子节点优先级-1fullPath: n.fullPath,}// Update maxParams (max of all children)for _, v : range child.children {if v.maxParams child.maxParams {child.maxParams v.maxParams}}n.children []*node{child}// []byte for proper unicode char conversion, see #65n.indices string([]byte{n.path[i]})n.path path[:i]n.handlers niln.wildChild falsen.fullPath fullPath[:parentFullPathIndexi]}// 将新来的节点插入新的parent节点作为子节点if i len(path) {path path[i:]if n.wildChild { // 如果是参数节点parentFullPathIndex len(n.path)n n.children[0]n.priority// Update maxParams of the child nodeif numParams n.maxParams {n.maxParams numParams}numParams--// 检查通配符是否匹配if len(path) len(n.path) n.path path[:len(n.path)] {// 检查更长的通配符, 例如 :name and :namesif len(n.path) len(path) || path[len(n.path)] / {continue walk}}pathSeg : pathif n.nType ! catchAll {pathSeg strings.SplitN(path, /, 2)[0]}prefix : fullPath[:strings.Index(fullPath, pathSeg)] n.pathpanic( pathSeg in new path fullPath conflicts with existing wildcard n.path in existing prefix prefix )}// 取path首字母用来与indices做比较c : path[0]// 处理参数后加斜线情况if n.nType param c / len(n.children) 1 {parentFullPathIndex len(n.path)n n.children[0]n.prioritycontinue walk}// 检查路path下一个字节的子节点是否存在// 比如s的子节点现在是earch和upportindices为eu// 如果新加一个路由为super那么就是和upport有匹配的部分u将继续分列现在的upport节点for i, max : 0, len(n.indices); i max; i {if c n.indices[i] {parentFullPathIndex len(n.path)i n.incrementChildPrio(i)n n.children[i]continue walk}}// 否则就插入if c ! : c ! * {// []byte for proper unicode char conversion, see #65// 注意这里是直接拼接第一个字符到n.indicesn.indices string([]byte{c})child : node{maxParams: numParams,fullPath: fullPath,}// 追加子节点n.children append(n.children, child)n.incrementChildPrio(len(n.indices) - 1)n child}n.insertChild(numParams, path, fullPath, handlers)return}// 已经注册过的节点if n.handlers ! nil {panic(handlers are already registered for path fullPath )}n.handlers handlersreturn} }整个路由树构造的详细过程 1第一次注册路由例如注册search 2继续注册一条没有公共前缀的路由例如blog 3注册一条与先前注册的路由有公共前缀的路由例如support 路由注册示例 package mainimport (github.com/gin-gonic/gin )func main() {r : gin.Default()// 注册静态路由r.GET(/hello, func(c *gin.Context) {c.String(200, Hello, World!)})// 注册动态路由r.GET(/user/:id, func(c *gin.Context) {id : c.Param(id)c.String(200, User ID: %s, id)})// 注册通配符路由r.GET(/static/*filepath, func(c *gin.Context) {filepath : c.Param(filepath)c.String(200, Filepath: %s, filepath)})r.Run(:8080) }4r.Run() 路由匹配 路由匹配是根据请求路径在 Radix 树中查找对应节点并执行处理函数的过程。 核心代码getValue getValue 方法负责在 Radix 树中查找路径 1Run()方法 / Run attaches the router to a http.Server and starts listening and serving HTTP requests. // It is a shortcut for http.ListenAndServe(addr, router) // Note: this method will block the calling goroutine indefinitely unless an error happens. func (engine *Engine) Run(addr ...string) (err error) {defer func() { debugPrintError(err) }()if engine.isUnsafeTrustedProxies() {debugPrint([WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.)}address : resolveAddress(addr)debugPrint(Listening and serving HTTP on %s\n, address)err http.ListenAndServe(address, engine.Handler())return }2Handler()方法处理类 func (engine *Engine) Handler() http.Handler {if !engine.UseH2C {return engine}h2s : http2.Server{}return h2c.NewHandler(engine, h2s) }3http.Handler type Handler interface {ServeHTTP(ResponseWriter, *Request) }4ServeHTTP实现 // gin.go func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {// 这里使用了对象池c : engine.pool.Get().(*Context)// 这里有一个细节就是Get对象后做初始化c.writermem.reset(w)c.Request reqc.reset()engine.handleHTTPRequest(c) // 我们要找的处理HTTP请求的函数engine.pool.Put(c) // 处理完请求后将对象放回池子 }5handleHTTPRequest方法 // gin.go func (engine *Engine) handleHTTPRequest(c *Context) {// 根据请求方法找到对应的路由树t : engine.treesfor i, tl : 0, len(t); i tl; i {if t[i].method ! httpMethod {continue}root : t[i].root// 在路由树中根据path查找value : root.getValue(rPath, c.Params, unescape)if value.handlers ! nil {c.handlers value.handlersc.Params value.paramsc.fullPath value.fullPathc.Next() // 执行函数链条c.writermem.WriteHeaderNow()return}c.handlers engine.allNoRouteserveError(c, http.StatusNotFound, default404Body) } 6getValue方法 路由匹配是由节点的 getValue方法实现的。getValue根据给定的路径(键)返回nodeValue值保存注册的处理函数和匹配到的路径参数数据。 如果找不到任何处理函数则会尝试TSR(尾随斜杠重定向)。 // tree.gotype nodeValue struct {handlers HandlersChainparams Params // []Paramtsr boolfullPath string }func (n *node) getValue(path string, po Params, unescape bool) (value nodeValue) {value.params po walk: // Outer loop for walking the treefor {prefix : n.pathif path prefix {// 我们应该已经到达包含处理函数的节点。// 检查该节点是否注册有处理函数if value.handlers n.handlers; value.handlers ! nil {value.fullPath n.fullPathreturn}if path / n.wildChild n.nType ! root {value.tsr truereturn}// 没有找到处理函数 检查这个路径末尾/ 是否存在注册函数indices : n.indicesfor i, max : 0, len(indices); i max; i {if indices[i] / {n n.children[i]value.tsr (len(n.path) 1 n.handlers ! nil) ||(n.nType catchAll n.children[0].handlers ! nil)return}}return}if len(path) len(prefix) path[:len(prefix)] prefix {path path[len(prefix):]// 如果该节点没有通配符(param或catchAll)子节点// 我们可以继续查找下一个子节点if !n.wildChild {c : path[0]indices : n.indicesfor i, max : 0, len(indices); i max; i {if c indices[i] {n n.children[i] // 遍历树continue walk}}// 没找到// 如果存在一个相同的URL但没有末尾/的叶子节点// 我们可以建议重定向到那里value.tsr path / n.handlers ! nilreturn}// 根据节点类型处理通配符子节点n n.children[0]switch n.nType {case param:// find param end (either / or path end)end : 0for end len(path) path[end] ! / {end}// 保存通配符的值if cap(value.params) int(n.maxParams) {value.params make(Params, 0, n.maxParams)}i : len(value.params)value.params value.params[:i1] // 在预先分配的容量内扩展slicevalue.params[i].Key n.path[1:]val : path[:end]if unescape {var err errorif value.params[i].Value, err url.QueryUnescape(val); err ! nil {value.params[i].Value val // fallback, in case of error}} else {value.params[i].Value val}// 继续向下查询if end len(path) {if len(n.children) 0 {path path[end:]n n.children[0]continue walk}// ... but we cantvalue.tsr len(path) end1return}if value.handlers n.handlers; value.handlers ! nil {value.fullPath n.fullPathreturn}if len(n.children) 1 {// 没有找到处理函数. 检查此路径末尾加/的路由是否存在注册函数// 用于 TSR 推荐n n.children[0]value.tsr n.path / n.handlers ! nil}returncase catchAll:// 保存通配符的值if cap(value.params) int(n.maxParams) {value.params make(Params, 0, n.maxParams)}i : len(value.params)value.params value.params[:i1] // 在预先分配的容量内扩展slicevalue.params[i].Key n.path[2:]if unescape {var err errorif value.params[i].Value, err url.QueryUnescape(path); err ! nil {value.params[i].Value path // fallback, in case of error}} else {value.params[i].Value path}value.handlers n.handlersvalue.fullPath n.fullPathreturndefault:panic(invalid node type)}}// 找不到如果存在一个在当前路径最后添加/的路由// 我们会建议重定向到那里value.tsr (path /) ||(len(prefix) len(path)1 prefix[len(path)] / path prefix[:len(prefix)-1] n.handlers ! nil)return} }Radix 树的路径匹配过程 package mainimport (fmt )type node struct {path stringchildren []*nodehandlers func() }func (n *node) addRoute(path string, handler func()) {child : node{path: path, handlers: handler}n.children append(n.children, child) }func (n *node) getRoute(path string) func() {for _, child : range n.children {if child.path path {return child.handlers}}return nil }func main() {root : node{}root.addRoute(/hello, func() {fmt.Println(Hello, World!)})handler : root.getRoute(/hello)if handler ! nil {handler() // 输出Hello, World!} else {fmt.Println(Route not found!)} } 总结 创建路由表 每种 HTTP 方法有独立的 Radix 树。路由通过 addRoute 插入到对应的树中。 处理 HTTP 请求 Gin 的入口是 Engine 的 ServeHTTP 方法。根据请求方法和路径查找路由节点 如果找到执行绑定的处理函数。如果未找到执行 404 处理函数。 分发请求 匹配成功的路由节点的处理函数会被依次执行支持中间件链。
http://www.dnsts.com.cn/news/24527.html

相关文章:

  • 网站开发工程师简介郑州网站建设贴吧
  • 企业网站的基本内容和营销功能sharepoint网站制作
  • 机械配件东莞网站建设沈阳网站seo排名
  • 长沙有哪些大型工厂中山seo代理商
  • 淄博网站建设 leda.ccwordpress中文对照
  • php做网站的重点开发者选项长期开启会怎样
  • 奉节集团网站建设域名命名网站
  • 电子商务网站建设与运营商城网站项目案例
  • 网站建设公司销售招聘哪个软件可以制作游戏
  • 自己能做企业网站吗工业和信息化部官网
  • 怎么给自己的网站做扫描码美丽说的网站建设
  • 网站建设标签网站建设好怎么优化
  • 做网站建设销售员准备什么qq是什么公司开发的
  • 域名到期了网站备案还有效吗食品包装设计理念
  • 京东门户网站怎么做电影网站建设教程
  • 个人博客网站制作代码社区网站做的比较好的有哪些
  • 注册服务器网站哪个好wordpress 做图片站
  • 哪些网站有搜索引擎作弊的东莞市建设监督网站
  • 建站公司最新价格做加盟网站赚钱吗
  • 网站开发 性能方面网上工作平台
  • 网站开发语言统计asp.net 获取网站的绝对路径
  • 网站广告代码检测企业网站优化平台
  • 网站免费高清素材软件学服装设计后悔死了
  • 图片演示dw做网站网络培训学习心得体会
  • 湖南seo网站策划建设网络平台的技术服务合同交印花税吗
  • 莱芜区网站wordpress SQL 配置
  • 旅游网站建设设计移动端网站优秀案例
  • 深圳松岗做网站网络优化行业的发展前景
  • 360网站做推广自己网站制作的详细教程
  • 网站开发还有哪些做k线图网站