手把手带你写一个 Web 框架
叶剑峰
腾讯高级工程师,前滴滴技术专家
22731 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 42 讲
特别放送 (1讲)
手把手带你写一个 Web 框架
15
15
1.0x
00:00/00:00
登录|注册

05|封装:如何让你的框架更好用?

Text
HTML
XML
JSONP
JSON
其他 Header
Cookie
返回状态码
Form 表单格式
XML 格式
JSON 格式
通用方法
Cookie 信息
请求域名
请求 IP
请求方法
请求地址
HTML 输出方法实现
JSONP 输出方法实现
Bind 请求方法实现
Param 请求方法实现
Query 请求方法实现
SetStatus
SetCookie
SetHeader
Redirect
Text
Html
Xml
Jsonp
Json
Cookie
Header
基础信息
其他格式
XML body
JSON body
Form 表单中带的参数
路由匹配中带的参数
请求地址中带的参数
Body 体
Header 头部
Body 信息
其他细节内容
HTTP 头部
XSS 攻击
先系统设计,再定义接口,最后具体实现
函数封装并不简单
返回相关接口实现
请求相关接口实现
IResponse 接口定义
IRequest 接口定义
封装返回数据
读取请求数据
思考题
小结
实现具体的接口
定义接口让封装更明确
思考如何封装请求和返回
封装:如何让你的框架更好用?

该思维导图由 AI 生成,仅供参考

你好,我是轩脉刃。
在前面几节课,我们实现了框架的路由、中间件等机制,并且自定义 context 结构来封装请求。但是回顾在对 context 的封装中,我们只是将 request、response 结构直接放入 context 结构体中,对应的方法并没有很好的封装。所以这节课,我们要做的事情就是为 context 封装更多的方法,让框架更好用。
在今天的学习中,希望你能认识到,函数封装并不是一件很简单、很随意的事情。相反,如何封装出易用、可读性高的函数是非常需要精心考量的,框架中每个函数的参数、返回值、命名,都代表着我们作为作者在某个事情上的思考。想要针对某个功能,封装出一系列比较完美的接口,更要我们从系统性的角度思考。

思考如何封装请求和返回

我们的目标是尽量在 context 这个数据结构中,封装“读取请求数据”和“封装返回数据”中的方法。在动手之前还是先做到心中有数,我们将请求和返回这两个事情分开思考。
读取请求数据
要读取请求数据包含哪些内容呢?第一讲我们提到过,HTTP 消息体分为两个部分:HTTP 头部和 HTTP Body 体。头部描述的一般是和业务无关但与传输相关的信息,比如请求地址、编码格式、缓存时长等;Body 里面主要描述的是与业务相关的信息。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了如何通过封装来提高框架的易用性,主要包括函数封装、请求和返回数据的封装、定义清晰的接口以及具体实现功能模块等方面。作者强调了封装出易用、可读性高的函数需要精心考量,并通过具体的代码示例展示了如何实现请求参数的获取和类型转换、路由参数的获取以及请求数据的解析和绑定。此外,还介绍了返回接口中 JSONP 输出的实现原理和方法。通过深入的技术讲解,帮助读者了解了如何通过封装来提高框架的易用性,以及如何定义接口和实现具体功能模块。文章内容丰富,涵盖了框架开发中的重要技术细节,对于想要深入了解框架设计和开发的读者具有很高的参考价值。文章还提出了一个思考题,引发读者对安全性和XSS攻击的思考,为读者提供了更多的技术思考和学习的机会。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《手把手带你写一个 Web 框架》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(11)

  • 最新
  • 精选
  • 不知道各位看github有没有看到 FormInt 和 FormString 我没有找到可能是粗心,建议各位在实现的时候可以在开头写上这么几句代码 var _ IRequest = new(Context) var _ IResponse = new(Context) var _ context.Context = new(Context) 如果当前context没有实现完 IRequest 和 IResponse 会报错并且你能看到是哪些接口未实现。 还有一个地方就是 Headers() map[string]string 这里接口定义的是 这样 但是试下你的时候是这样 Headers() map[string][]string

    作者回复: 确实是个好办法,直接做一下强制转换能将错误提前暴露 感谢

    2021-12-02
    11
  • qinsi
    转义是为了防止callback名称包含任意js代码。虽然要达成XSS有各种前置条件,防御XSS也要分各种情况,没有完美的方法,但转义总比不转义要强。 疑问: 1. JSONP的响应也是在拼接字符串,是否也可以用template? 2. 对于各种请求和响应格式的支持,是否也可以写成中间件的形式,让API更精简? 3. 重复读取body的场景有哪些?我能想到的是某些访问控制的中间件可能需要事先检查body的内容。这样是不是就没法做流式处理(因为要把请求body读完才能做下一步的处理)?比如一边读取请求body一边写入响应body。

    作者回复: 疑问: 1. JSONP的响应也是在拼接字符串,是否也可以用template? 当然可以的,拼接字符串有多种方式,template也是可以。 2. 对于各种请求和响应格式的支持,是否也可以写成中间件的形式,让API更精简? 可以的,修改响应格式主要要先修改下context中的responseWriter,让输出先进入到我们定义的writer中,然后修改后再输出到前端。这样做可行。 不过更多的做法是封装一个公共方法,所有api地方调用这个方法。 3. 重复读取body的场景有哪些?我能想到的是某些访问控制的中间件可能需要事先检查body的内容。这样是不是就没法做流式处理(因为要把请求body读完才能做下一步的处理)?比如一边读取请求body一边写入响应body。 这个场景很多的,要打一个输入请求的所有日志到日志管理系统中,或者要实现一个回放功能,都有可能要读取请求body.

    2021-09-22
    3
  • 咸鱼
    有两点疑惑,为什么要使用不带error返回值的cast方法呢,这样如果解析出了问题,返回的不会是默认值,而会是零值。另外一点是我记得前面注册路由的时候把路径都变成了大写来着,这样在取路径参数的时候应该加一道把key转成大写的步骤吧,不然会找不到参数的

    作者回复: 1 这里其实有三个逻辑,a 是否有这个key,b 这个key是否可以转换为目标对象, c 如果失败和没有,要怎么处理。目前是没有key返回默认值,有key但是无法转换返回零值,不过思考了一下,这里确实是改成“失败和处理”都返回默认值比较好。 2 parseParamsFromEndNode 这里传递的是request.URL.Path,是没有大写转换过的。应该不需要加这个

    2021-12-12
    1
  • taoist
    geekbang/05 分支里 c.SetOkStatus().Json("xxx") Content-Type不能被正确设置位application/json, 因为SetOkStatus里先执行了WriteHeader,正确做法是更新完Header后再执行 WriteHeader。 https://github.com/golang/go/issues/17083
    2023-12-15归属地:山东
    1
    1
  • 木移
    subjectApi.Put("/:id/:id", SubjectUpdateController) 这种路由取参数就没法区分了,注册路由需要把这种情况考虑进去吗
    2021-10-27
    2
    1
  • 高。
    request.go 中的BindJson,BindXml这类方法真的有效么?!
    2022-11-05归属地:上海
  • airmy丶
    输出解析HTML的这个地方:t, err := template.New(“output”).ParseFiles(file) 这里是否有问题?template.New 需要传入的应该是模版的文件名称吧?这里是否应该使用 filepath.Base(file) 去获取?
    2022-09-20归属地:广东
  • 老兵
    利用escape template.JSEscapeString(callbackFunc) 解决XSS攻击的问题。 疑问: 但是框架如何解决CSRF攻击的问题呢?检查CSRF token和XSRF token?
    2022-02-24
  • 牛玉富
    clientIp拼错了
    2022-01-13
  • kfns0b11
    > // 重新填充 request.Body,为后续的逻辑二次读取做准备 > ctx.request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) 这里能详细解释一下吗?ctx.request.Body 本身是 io.ReadCloser 类型,使用 NopCloser 函数为后续的逻辑二次读取做准备是什么意思啊?
    2021-11-16
    1
收起评论
显示
设置
留言
11
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部