手把手和你一起实现一个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. python之struct详解

    python之struct详解 2018-05-23 18:20:29 醉小义 阅读数 20115更多 分类专栏: python   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议 ...

  2. Docker:Centos7更新yum源下载docker

    前言: Docker 要求 CentOS 系统(6.5及以上)的内核版本高于 3.10 ,查看本页面的前提条件来验证你的CentOS 版本是否支持 Docker . 通过 uname -r 命令查看你 ...

  3. Hadoop0.20.2中MapReduce读取gb2312文件出现乱码问题

    单位用的是Linux系统的字符编码是gb2312,所以生成的文件都是按照默认编码生成的.给我的文件也都是gb2312的,在hadoop中运行mapreduce出现乱码,在网上查资料说是因为hadoop ...

  4. WPF教程六:理解WPF中的隧道路由和冒泡路由事件

    WPF中使用路由事件升级了传统应用开发中的事件,在WPF中使用路由事件能更好的处理事件相关的逻辑,我们从这篇开始整理事件的用法和什么是直接路由,什么是冒泡路由,以及什么是隧道路由. 事件最基本的用法 ...

  5. Pandas高级教程之:自定义选项

    目录 简介 常用选项 get/set 选项 经常使用的选项 最大展示行数 超出数据展示 最大列的宽度 显示精度 零转换的门槛 列头的对齐方向 简介 pandas有一个option系统可以控制panda ...

  6. python之数据驱动ddt操作(方法三)

    import unittestfrom selenium import webdriverfrom selenium.webdriver.common.by import Byimport unitt ...

  7. VSCode 如何远程连接其他主机的 WSL2

    VSCode 如何远程连接其他主机的 WSL2 VSCode 的 Remote Deployment 插件对 WSL2 直接提供了支持,能够很方便的连接本机的 WSL2 ,但是并没有提供一个连接远程 ...

  8. Error:Connection activation failed: No suitable device found for this connection 问题最新解决方案

    虽然网上有很多关于这个问题的解决方案,但是我还是决定自己再次重复写一下这个解决的方案,重在更新知识和了解VMware workstation 15新功能. 在使用VMware workstation克 ...

  9. sessionfilter中的拦截项判断

  10. vue目首屏添加skeleton骨架屏

    1. 安装插件:npm install vue-skeleton-webpack-plugin 2. 在src目录下创建 Skeleton.vue <template> <div c ...