手把手和你一起实现一个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. ansible 任务执行

    ansible 任务执行模式 Ansible 系统由控制主机对被管节点的操作方式可分为两类,即adhoc和playbook: ad-hoc模式(点对点模式)• ad-hoc模式(点对点模式) 使用单个 ...

  2. Spring:Spring中bean的生命周期

    Spring中,从BeanFactory或ApplicationContext取得的实例为Singleton(单例模式),就是预设为每一个Bean的别名只能维持一个实例,而不是每次都产生一个新的对象使 ...

  3. shell运维习题训练

    注:初学shell,以下为本人自己写的答案,如果有更好的,请指教! 1. 求2个数之和: 2. 计算1-100的和 3. 将一目录下所有的文件的扩展名改为bak 4.编译并执行当前目录下的所有.c文件 ...

  4. 2012年第三届蓝桥杯C/C++程序设计本科B组省赛 方阵旋转(代码填空)

    方阵旋转 对一个方阵转置,就是把原来的行号变列号,原来的列号变行号 例如,如下的方阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 转置后变为: 1 5 9 13 2 ...

  5. 使用xcode实现IM的那些坑

    想用xcode基于XMPP实现即时通讯,mac必须安装openfire(xmpp服务器),mysql(本地数据库,用于配置openfire),JDK(打开openfire必须本地具备java环境),x ...

  6. 物理机连接虚拟机中的数据库及Windows添加防火墙允许端口详细操作步骤

    公司项目中因为会使用到SQL server数据库,但是自己电脑无论安装2008R2或者2014版本都不成功,我想可能是和之前安装的一些Windows的软件存在冲突. 于是便单独创建了一台虚拟机,在虚拟 ...

  7. mysql 按照年统计数据并存到新表中

    参考:https://blog.csdn.net/u013201439/article/details/78116575 CREATE TABLE count_year SELECT YEAR(req ...

  8. c语言函数的嵌套使用和矩阵运算

    这段时间,听刚刚学习c的同学说函数嵌套运用不太熟练,想做一个简单的程序进行练习,我也就当练练手了,哈哈.虽然说是比较简单,但是其中的思维也是值得思考的. 一.函数的嵌套使用 简单说明题目:对于等式 y ...

  9. 【学习笔记】Tensor多维数组和axis的理解

    Tensor多维数组和axis的理解 今天在编写程序的时候一直对于axis=0或等于1搞不明白,这样对于整个numpy或者是tensorflow的基本运算和数据处理都会很模糊,所以花了一些时间来搞清楚 ...

  10. PAT乙级:1076 Wifi密码 (15分)

    PAT乙级:1076 Wifi密码 (15分) 题干 下面是微博上流传的一张照片:"各位亲爱的同学们,鉴于大家有时需要使用 wifi,又怕耽误亲们的学习,现将 wifi 密码设置为下列数学题 ...