手把手和你一起实现一个Web框架实战——EzWeb框架(二)[Go语言笔记]Go项目实战

代码仓库:

github

gitee

中文注释,非常详尽,可以配合食用

上一篇文章我们实现了框架的雏形,基本地实现了将原来的处理方法和监听处理的实例指向我们自定义的实例。封装出了GET,POST处理方法。完成了框架雏形。

本篇文章,我们将原本的handler方法中的参数 w http.ResponseWriter, req *http.Request 封装到一个新的结构体Context中,同时替代掉原来的参数,并在该结构体提供响应的一些简单的请求数据查询功能和响应处理方法。能够让我们快速获取数据以及构造响应。

一、设计这个Context

/*
@Time : 2021/8/17 下午1:46
@Author : Mrxuexi
@File : context.go
@Software: GoLand
*/
package Ez import (
"encoding/json"
"net/http"
) // H 为map[string]interface{}结构体起个别名,方便用户在代码中构建JSON
type H map[string]interface{} // Context 结构体,内部封装了 http.ResponseWriter, *http.Request
type Context struct {
Writer http.ResponseWriter
Req *http.Request
//请求的信息,包括路由和方法
Path string
Method string
//响应的状态码
StatusCode int
} //Context构造方法
func newContext(w http.ResponseWriter, req *http.Request) *Context {
return &Context{
Writer: w,
Req: req,
Path: req.URL.Path,
Method: req.Method,
}
} // 访问参数的处理方法PostForm和Query // PostForm 根据key拿到请求中的表单内容
func (c Context) PostForm(key string) string {
return c.Req.FormValue(key)
} // Query 根据key获取请求中的参数
func (c Context) Query(key string) string {
return c.Req.URL.Query().Get(key)
} //一些服用的前值方法Status处理响应状态码 SetHeader处理响应消息头 // Status 将状态码写入context,同时将通过封装起来的http.ResponseWriter方法,将状态码写入响应头
func (c Context) Status(code int) {
c.StatusCode = code
c.Writer.WriteHeader(code)
} // SetHeader 构造响应的消息头
func (c Context) SetHeader(key string,value string) {
c.Writer.Header().Set(key,value)
} // String 调用我们的 SetHeader和Status 方法,构造string类型响应的状态码和消息头,然后将字符串转换成byte写入到响应头
func (c Context) String(code int,values ...interface{}) {
c.SetHeader("Content-Type","text/plain")
c.Status(code)
var str = ""
for _, value := range values {
str += value.(string)
}
c.Writer.Write([]byte(str))
} // JSON 调用我们的 SetHeader和Status 方法,构造JSON类型响应的状态码和消息头,根据我们传入的对象来构造json数据写入
func (c Context) JSON(code int,obj interface{}) {
c.SetHeader("Content-Type","application/json")
c.Status(code)
encoder := json.NewEncoder(c.Writer)
if err := encoder.Encode(obj); err != nil {
http.Error(c.Writer, err.Error(),http.StatusInternalServerError)
}
} // Data 同上 ,但是直接写入字节数组,不再构建消息头
func (c Context) Data(code int,data []byte) {
c.Status(code)
c.Writer.Write(data)
} // HTML 模版渲染 同上,消息体传入的是html文件
func (c Context) HTML(code int, html string) {
c.SetHeader("Content-Type","text/html")
c.Status(code)
c.Writer.Write([]byte(html))
}

我们将这些内容全部封装到了Context中,能够让我们快速获取数据以及构造响应。

如果我们没有进行封装,构造一个Json响应是十分麻烦的:

1、构造一个消息对象

//新建obj存储信息
obj := map[string]interface{}{
"name" : "Mrxuexi",
"password" : "password",
}

2、构造相应的消息头

//构造响应的消息头
w.Header().Set("Content-Type","application/json")

3、写入响应的状态码

//w写入状态码
w.WriteHeader(http.StatusOK)

4、根据已经做好的响应writer构建一个json编码器

//根据w建立一个json编码器
encoder := json.NewEncoder(w)

5、根据obj构建响应的消息体

//将obj放入消息体,处理报错
if err := encoder.Encode(obj); err != nil {
http.Error(w, err.Error(), 500)
}

如果我们进行了封装,我们只需要这样,省去了许多繁杂的工作:

c.JSON(http.StatusOK, context.H{
"name" : "Mrxuexi",
"password" : "password",
})

二、重新调整封装入口

我们将原来的Ez.go进行重构,将原来的路由表router map 封装到router.go。

HandlerFunc传入的参数改为 *Context

type HandlerFunc func(*Context)

将原来的ServeHTTP 方法作为入口,将请求传入构造context。

在router.go中进行addRoute路由注册处理和对应的请求处理。

/*
@Time : 2021/8/16 下午4:03
@Author : Mrxuexi
@File : Ez
@Software: GoLand
*/ package Ez import (
"net/http"
) // HandlerFunc 是Ez框架中定义的对请求的响应处理方法,传入*Context针对http请求处理
type HandlerFunc func(*Context) // Engine 实现了"net/http"标准库中的 Handler 接口中的ServeHTTP方法
type Engine struct {
//用于存储路由处理方法
//key是方法类型加路径,value是用户的处理方法
router *router
} // ServeHTTP 方法的实现,用于实现处理HTTP请求
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
//根据req和w实例一个context
c := newContext(w, req)
//传入开始执行处理
engine.router.handle(c)
} // New 路由存储结构的构造函数
func New() *Engine {
return &Engine{router: newRouter()}
} // addRoute 方法封装在router中,在 router map[string]HandlerFunc 中存入对应处理方法
func (engine *Engine) addRoute(method string, path string, handler HandlerFunc) {
engine.router.addRoute(method, path, handler)
} // GET 实现的是注册GET请求的路径和对应方法,调用了addRoute,存入了route 结构体的handler中
func (engine *Engine) GET(path string, handler HandlerFunc) {
engine.addRoute("GET", path, handler)
} // POST 同上
func (engine *Engine) POST(path string, handler HandlerFunc) {
engine.addRoute("POST", path, handler)
} func (engine *Engine) Run(addr string) (err error) {
return http.ListenAndServe(addr, engine)
}

三、重新调整封装router,路由注册处理和请求处理

所有的路由注册处理和请求处理都从入口拿到这一层来处理。

/*
@Time : 2021/8/16 下午4:03
@Author : Mrxuexi
@File : Ez
@Software: GoLand
*/ package Ez import (
"net/http"
) // HandlerFunc 是Ez框架中定义的对请求的响应处理方法,传入*Context针对http请求处理
type HandlerFunc func(*Context) // Engine 实现了"net/http"标准库中的 Handler 接口中的ServeHTTP方法
type Engine struct {
//用于存储路由处理方法
//key是方法类型加路径,value是用户的处理方法
router *router
} // ServeHTTP 方法的实现,用于实现处理HTTP请求
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
//根据req和w实例一个context
c := newContext(w, req)
//传入开始执行处理
engine.router.handle(c)
} // New 路由存储结构的构造函数
func New() *Engine {
return &Engine{router: newRouter()}
} // addRoute 方法封装在router中,在 router map[string]HandlerFunc 中存入对应处理方法
func (engine *Engine) addRoute(method string, path string, handler HandlerFunc) {
engine.router.addRoute(method, path, handler)
} // GET 实现的是注册GET请求的路径和对应方法,调用了addRoute,存入了route 结构体的handler中
func (engine *Engine) GET(path string, handler HandlerFunc) {
engine.addRoute("GET", path, handler)
} // POST 同上
func (engine *Engine) POST(path string, handler HandlerFunc) {
engine.addRoute("POST", path, handler)
} func (engine *Engine) Run(addr string) (err error) {
return http.ListenAndServe(addr, engine)
}

测试:

/*
@Time : 2021/8/16 下午4:01
@Author : mrxuexi
@File : main
@Software: GoLand
*/
package main import (
"Ez"
"net/http"
)
func main() {
r := Ez.New()
r.GET("/", func(c *Ez.Context) {
c.HTML(http.StatusOK,"<h1>This is the index</h1>")
})
r.GET("/hello", func(c *Ez.Context) {
c.String(http.StatusOK, "hello")
})
r.POST("/message", func(c *Ez.Context) {
c.JSON(http.StatusOK,Ez.H{
"name" : c.PostForm("name"),
"age" : c.PostForm("age"),
})
}) r.Run(":9090")
}

index:

GET请求hello:

POST请求hello:

成功!

接上期灵魂画手环节:

参考:

[1]: https://github.com/geektutu/7days-golang/tree/master/gee-web ""gee""

[2]: https://github.com/gin-gonic/gin ""gin""

手把手和你一起实现一个Web框架实战——EzWeb框架(二)[Go语言笔记]Go项目实战的更多相关文章

  1. 手把手和你一起实现一个Web框架实战——EzWeb框架(三)[Go语言笔记]Go项目实战

    手把手和你一起实现一个Web框架实战--EzWeb框架(三)[Go语言笔记]Go项目实战 代码仓库: github gitee 中文注释,非常详尽,可以配合食用 本篇代码,请选择demo3 这一篇文章 ...

  2. 手把手和你一起实现一个Web框架实战——EzWeb框架(四)[Go语言笔记]Go项目实战

    手把手和你一起实现一个Web框架实战--EzWeb框架(四)[Go语言笔记]Go项目实战 代码仓库: github gitee 中文注释,非常详尽,可以配合食用 这一篇文章主要实现路由组功能.实现路由 ...

  3. 手把手和你一起实现一个Web框架实战——EzWeb框架(五)[Go语言笔记]Go项目实战

    手把手和你一起实现一个Web框架实战--EzWeb框架(五)[Go语言笔记]Go项目实战 代码仓库: github gitee 中文注释,非常详尽,可以配合食用 本篇代码,请选择demo5 中间件实现 ...

  4. Go语言笔记[实现一个Web框架实战]——EzWeb框架(一)

    Go语言笔记[实现一个Web框架实战]--EzWeb框架(一) 一.Golang中的net/http标准库如何处理一个请求 func main() { http.HandleFunc("/& ...

  5. 用C写一个web服务器(三) Linux下用GCC进行项目编译

    .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...

  6. MVC框架入门准备(二) - 语言包类与工厂模式

    语言包类: 相关配置项 LANG_FOLDER : 语言包目录名,默认为Lang LANG_AUTO : 自动识别语言,默认为true DEFAULT_LANG : 默认语言,默认为zh-cn方法 取 ...

  7. Python之路,Day18 - 开发一个WEB聊天来撩妹吧

    Python之路,Day18 - 开发一个WEB聊天来撩妹吧   本节内容: 项目实战:开发一个WEB聊天室 功能需求: 用户可以与好友一对一聊天 可以搜索.添加某人为好友 用户可以搜索和添加群 每个 ...

  8. 【转载】ASP.NET MVC Web API 学习笔记---第一个Web API程序

    1. Web API简单说明 近来很多大型的平台都公开了Web API.比如百度地图 Web API,做过地图相关的人都熟悉.公开服务这种方式可以使它易于与各种各样的设备和客户端平台集成功能,以及通过 ...

  9. ASP.NET MVC Web API 学习笔记---第一个Web API程序

    http://www.cnblogs.com/qingyuan/archive/2012/10/12/2720824.html GetListAll /api/Contact GetListBySex ...

随机推荐

  1. 前端 | Vue 路由返回恢复页面状态

    需求场景:首页搜索内容,点击跳转至详情页,页面后退返回主页,保留搜索结果. 方案:路由参数:路由守卫 需求描述 在使用 Vue 开发前端的时候遇到一个场景:在首页进行一些数据搜索,点击搜索结果进入详情 ...

  2. 苦B程序员的数据验证之路

    发生了什么事 在一次苦B程序员和苦C程序员的结对编程中发生的一段对话 代码是这样的: public void deleteAllExtendAclsFromContent(String content ...

  3. 创建者模式 -- 单例模式(反射&序列化)

    看的视频:https://www.bilibili.com/video/av43896218/?p=286 实现方式:(构造器私有,提供一个外部可以访问的方法(可以提供实例)) 1.饿汉式:线程安全, ...

  4. Linux之 du的用法

    du 显示目录和文件的大小,常用命令为 du -sh * du -sm * | sort -n //统计当前目录大小 并按大小 排序 du 无参数 显示当前路径下的目录大小和子目录大小 -b/-k/- ...

  5. 深入理解Java并发类——AQS

    目录 什么是AQS 为什么需要AQS AQS的核心思想 AQS的内部数据和方法 如何利用AQS实现同步结构 ReentrantLock对AQS的利用 尝试获取锁 获取锁失败,排队竞争 参考 什么是AQ ...

  6. 1.前言-聊聊Java这条路

    一.解决大家的疑问 1.零基础学习编程? 有编程基础的比零基础的困难,毕竟有一些固定思维 目标:爱好.做网站.做游戏 2.英语不好能学吗? 程序并没有大家想象的那么多英语,天天都在用,慢慢就掌握了 3 ...

  7. MySQL字符串操作函数

    使用方法:concat(str1,str2,-)   返回结果为连接参数产生的字符串.如有任何一个参数为NULL ,则返回值为 NULL. mysql> select concat('11',' ...

  8. [刘阳Java]_处理并发有哪些方法

    1.HTML静态化 ,将活动页面上的所有可以静态的元素全部静态化,并尽量减少动态元素2.禁止重复提交:用户提交之后按钮置灰,禁止重复提交3.用户限流:在某一时间段内只允许用户提交一次请求,比如可以采取 ...

  9. 使用jquery的on方法注册事件遇到的坑

    1,使用on注册事件 $(selector).on(event,childSelector,data,function) 2,$(selector)中的selector可以是document,那么意味 ...

  10. windows程序快速启动的方式:WIN键+R

    WIN键+R是windows快速启动程序的一种方式,一般能独立运行的程序都能以这种方式启动.如notepad.calc.explorer等程序. 在命令行方式下explorer加上不同的参数,会得到不 ...