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

一家专门做原型的网站邯郸网站建设服务报价

一家专门做原型的网站,邯郸网站建设服务报价,网站建设素材图片,特价锦州网站建设文章目录 模块化路由前缀树路由 前情提示#xff1a; 【Golang学习笔记】从零开始搭建一个Web框架#xff08;一#xff09;-CSDN博客 模块化路由 路由在kilon.go文件中导致路由和引擎交织在一起#xff0c;如果要实现路由功能的拓展增强#xff0c;那将会非常麻烦… 文章目录 模块化路由前缀树路由 前情提示 【Golang学习笔记】从零开始搭建一个Web框架一-CSDN博客 模块化路由 路由在kilon.go文件中导致路由和引擎交织在一起如果要实现路由功能的拓展增强那将会非常麻烦这无疑降低了代码的可读性和可维护性。现在的工作是将路由从引擎里剥离出来引擎中仅对路由进行包装。 新建文件router.go当前目录结构为 myframe/├── kilon/│ ├── context.go│ ├── go.mod [1]│ ├── kilon.go│ ├── router.go├── go.mod [2]├── main.go在router中添加下面内容 package kilonimport (net/http )type router struct {Handlers map[string]HandlerFunc } // 创建router对象 func newRouter() *router {return router{make(map[string]HandlerFunc)} } // 剥离路由注册的具体实现 func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {key : method - patternr.Handlers[key] handler } // 剥离SeverHTTP中路由处理的具体实现 func (r *router) handle(ctx *Context) {key : ctx.Method - ctx.Pathif handler, ok : r.Handlers[key]; ok {handler(ctx)} else {ctx.String(http.StatusNotFound, 404 NOT FOUND: %s\n, ctx.Path)} }修改kilon.go文件 package kilonimport (net/http )type HandlerFunc func(*Context)type Origin struct {router *router // 修改路由 }func New() *Origin {return Origin{router: newRouter()} // 修改构造函数 }func (origin *Origin) addRoute(method string, pattern string, handler HandlerFunc) {origin.router.addRoute(method, pattern, handler) // 修改调用 }func (origin *Origin) GET(pattern string, hander HandlerFunc) {origin.addRoute(GET, pattern, hander) }func (origin *Origin) POST(pattern string, hander HandlerFunc) {origin.addRoute(POST, pattern, hander) }func (origin *Origin) ServeHTTP(w http.ResponseWriter, req *http.Request) {ctx : newContext(w, req)origin.router.handle(ctx) // 调用router.go中的处理方法 }func (origin *Origin) Run(addr string) (err error) {return http.ListenAndServe(addr, origin) }至此实现了路由的模块化后续路由功能的增强将不会改动kilon.go文件。 前缀树路由 目前的路由表使用map存储键值对索引非常高效但是有一个弊端键值对的存储的方式只能用来索引静态路由而无法实现动态路由。在实际的应用中可能需要使用正则表达式或者其他匹配规则来实现更复杂的路由匹配而 map 无法提供这种功能。接下来将使用前缀树Tire树实现动态路由主要实现两个功能 参数匹配:。例如 /p/:name/doc可以匹配 /p/zhangsan/doc 和 /p/lisi/doc。通配*仅允许最后一个有*号。例如 /static/*filepath可以匹配/static/fav.ico和/static/js/jQuery.js。 新建文件trie.go当前文件目录结构为 myframe/├── kilon/│ ├── context.go│ ├── go.mod [1]│ ├── kilon.go│ ├── router.go│ ├── tire.go├── go.mod [2]├── main.go在trie.go中创建前缀树的节点: type node struct {patten string // 待匹配路由part string // 路由当前部分children []*node // 孩子节点isWild bool // 是否为模糊搜索当含有:和通配符*时为true }当注册路由/p/:name/doc、“/p/:name/png”、“/p/:lang/doc”、/p/:lang/png后树中内容如下 可以看到pattern只有在插入最后一个子节点后才会设置这是为了在查询路由信息时可以根据 pattern来判断改路由是否注册。isWaild的作用在于当part不匹配时如果isWaild为true可以继续搜索这样就实现了模糊匹配。 先实现路由注册时的前缀树插入逻辑 func (n *node) insert(pattern string, parts[]string, index int)pattern是注册路由地址parts是解析pattern后的字符串数组使用方法strings.Split(pattern, /)进行解析如/p/:name/doc对应 [“p”,“:name”,“doc”]parts[index]是当前需要插入的part。可以通过index判断是否退出。疑问如果只用Split解析那pattren/的时候不就无法注册了吗答开始时树的根节点的part为空不会匹配“p一定会插入到根节点的子节点切片中。而当pattern为”/“时解析字符串切片为空进入根节点的时候len(parts) index 0,会将根节点的pattern设置为”/“也可以实现路由”/的注册。 代码如下 func (n *node) insert(pattern string, parts[]string, index int){// 进来的时候说明 n.part parts[index-1] 即最后一个 part 则直接设置 pattenif len(parts) index {n.patten patternreturn}// 还需匹配 part// 先在 n.children 切片中匹配 partpart : parts[index]child : n.matchChild(part)// 如果没有找到则构建一个 child 并插入 n.children 切片中if child nil {child node{part: part,// 含有:或者通配符*时为 trueisWild: part[0] : || part[0] *,}// 插入 n.children 切片n.children append(n.children, child)}// 递归插入child.insert(pattern, parts, index 1) } // 查找匹配 child func (n *node) matchChild(part string) *node {// 遍历 n.children 查找 part 相同的 childfor _, child : range n.children {// 如果找到匹配返回 child 当 isWild 为 true 时视为匹配实现模糊搜索if child.part part || child.isWild true {return child}} // 没找到返回nilreturn nil }接下来实现接受请求时查询路由信息时的前缀树搜索逻辑 func (n *node) search(parts []string, index int) *nodeparts是路由地址的解析数组index指向当前part索引 代码如下: // 搜索 func (n *node) search(parts []string, index int) *node {// 如果匹配将节点返回if len(parts) index || strings.HasPrefix(n.part, *) {if n.pattern {return nil}return n}part : parts[index]// 获取匹配的所有孩子节点nodes : n.matchChildren(part)// 递归搜索匹配的child节点for _, child : range nodes {result : child.search(parts, index1)if result ! nil {return result}}return nil } // 查找匹配的孩子节点由于有:和*所以可能会有多个匹配因此返回一个节点切片 func (n *node) matchChildren(part string) []*node {nodes : make([]*node, 0)for _, child : range n.children {if child.part part || child.isWild true {nodes append(nodes, child) // 将符合的孩子节点添入返回切片}}return nodes }至此trie.go暂时写完现在在路由中进行应用回到router.go文件。为了区分不同的方法如GET和POST为每一个Method建立一颗前缀树并以键值对的形式存储在一个map中map[Method] tire。修改router结构体与构造方法 type router struct {roots map[string]*node // 前缀树mapHandlers map[string]HandlerFunc // 将pattern作为key获取/注册方法 } func newRouter() *router {return router{make(map[string]*node),make(map[string]HandlerFunc),} }将pattern插入前缀树之前要先解析成字符串切片现在需要实现一个解析函数。 func parsePattern(pattern string) []string {temp : strings.Split(pattern, /)parts : make([]string, 0)for _, item : range temp {if item ! {parts append(parts, item)if item[0] * {break}} }return parts }修改注册路由的逻辑 func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {parts : parsePattern(pattern) // 解析patternkey : method - patternif _, ok : r.roots[key]; !ok {r.roots[method] node{} // 如果没有则创建一个节点}r.roots[method].insert(pattern, parts, 0) // 前缀树插入patternr.Handlers[key] handler // 注册方法 }当接受请求时需要对请求中携带的路由信息解析并获取匹配的节点以及:“,”*匹配到的参数现在需要写一个路由获取方法 func (r *router) getRoute(method string, path string) (*node, map[string]string) {searchParts : parsePattern(path) // 解析路由信息params : make(map[string]string) // 参数字典root, ok : r.roots[method]if !ok {return nil, nil}// 搜索匹配节点n : root.search(searchParts, 0)if n! nil {parts : parsePattern(n.pattern) // 解析pattern// 寻找*和:,找到对应的参数。for index, part : range parts {if part[0] : {params[part[1:]] searchParts[index]}if part[0] * len(part) 1 {// 将*后切片内容拼接成路径params[part[1:]] strings.Join(searchParts[index:],/)break // 仅允许一个通配符*}return n, params}}return nil, nil }路径中的参数应该交给上下文对象让用户便捷获取。在Context结构体中添加Params属性,并包装获取方法 type Context struct {Writer http.ResponseWriterReq *http.RequestPath stringMethod stringParams map[string]string // 路由参数属性StatusCode int } // 获取路径参数 func (c *Context) Param(key string) string {value : c.Params[key]return value }在router.go中的handle中应用路由获取方法并将路径参数提交给上下文对象。 func (r *router) handle(ctx *Context) {n, params : r.getRoute(ctx.Method, ctx.Path) // 获取路由节点及参数字典ctx.Params paramsif n ! nil {key : ctx.Method - n.pattern // key为n的patternr.Handlers[key](ctx) // 调用注册函数} else {ctx.String(http.StatusNotFound, 404 NOT FOUND: %s\n, ctx.Path)} }现在router.go内容为 package kilonimport (net/httpstrings )type router struct {roots map[string]*nodeHandlers map[string]HandlerFunc }func newRouter() *router {return router{make(map[string]*node),make(map[string]HandlerFunc),} }func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {parts : parsePattern(pattern)key : method - pattern_, ok : r.roots[method]if !ok {r.roots[method] node{}}r.roots[method].insert(pattern, parts, 0)r.Handlers[key] handler }func (r *router) handle(ctx *Context) {n, params : r.getRoute(ctx.Method, ctx.Path)ctx.Params paramsif n ! nil {key : ctx.Method - n.patternr.Handlers[key](ctx)} else {ctx.String(http.StatusNotFound, 404 NOT FOUND: %s\n, ctx.Path)} }func parsePattern(pattern string) []string {temp : strings.Split(pattern, /)parts : make([]string, 0)for _, item : range temp {if item ! {parts append(parts, item)if item[0] * {break}}}return parts }func (r *router) getRoute(method string, path string) (*node, map[string]string) {searchParts : parsePattern(path)params : make(map[string]string)root, ok : r.roots[method]if !ok {return nil, nil}n : root.search(searchParts, 0)if n ! nil {parts : parsePattern(n.pattern)for index, part : range parts {if part[0] : {params[part[1:]] searchParts[index]}if part[0] * len(part) 1 {params[part[1:]] strings.Join(searchParts[index:], /)break}}return n, params}return nil, nil }在main.go测试一下 package mainimport (kilonnet/http )func main() {r : kilon.New()r.GET(/hello, func(ctx *kilon.Context) {ctx.JSON(http.StatusOK, kilon.H{message: Hello World,})})r.GET(/hello/:username, func(ctx *kilon.Context) {ctx.JSON(http.StatusOK, kilon.H{message: ctx.Param(username),})})r.GET(/hello/:username/*filename, func(ctx *kilon.Context) {ctx.JSON(http.StatusOK, kilon.H{username: ctx.Param(username),filename: ctx.Param(filename),})})r.Run(:8080) }分别访问下面地址都可以看到响应信息 127.0.0.1:8080/hello 127.0.0.1:8080/hello/zhangsan 127.0.0.1:8080/hello/zhangsan/photo.png
http://www.hkea.cn/news/14356576/

相关文章:

  • 上海网站建设系wordpress魔板
  • 长沙市住房和建设局官方网站家居网站建设的需求分析
  • 最牛的科技网站建设帝国cms 做的博客网站
  • 融资网站建设重点抖音关键词优化排名
  • 网站设计素材下载网站建设都需要什么资料
  • 常州新北建设局网站做落地页素材在什么网站上找
  • 广州快速建站公司推荐东莞网站开发哪家强
  • 无锡网站的优化哪家好16素材网
  • 临沭县建设局官方网站磐安做网站
  • 网站备案更改吗荆楚网微信公众平台下载
  • 昆明网站建设哪家公司好企业网站优化怎么做
  • 长沙营销型网站建设公司网站设置主网
  • 药品网站如何建设工信部备案查询网站
  • 柳州网站建设22如何 在网站上面做推广
  • .design 域名的网站建设网站要做的工作总结
  • 网站增值服务网站设计说明书主要有什么
  • 网站开发的开题报告引言安装完wordpress怎么打开
  • 泉州模板网站建站wordpress 文章列表目录颜色
  • 登录官方网站帮人做钓鱼网站的人
  • 恶意网站的防治2017商会网站建设方案
  • 新绛做网站南昌seo搜索优化
  • 设计工作室 网站深圳集团网站建设企业
  • 外贸网站推广平台排名interiart wordpress
  • 网站建设财务分析南宁seo公司
  • 济宁做网站多少钱移动端 pc网站开发
  • 房地产网站建设方案书网站二级目录解析
  • 网站统计器有道云笔记 wordpress
  • 天津哪家做企业网站广州免费接种宫颈癌疫苗
  • 评论给网站带来的益处网站代运营做哪些
  • 东莞网站设计实力wdcp 无法访问此网站