5.10 Context-总结与面试要点
本节课工程结构如下:
(base) yanglei@yuanhong 10-summary % tree ./
./
├── context.go
├── context_test.go
├── go.mod
├── go.sum
├── handleFunc.go
├── httpServer.go
├── httpServer_test.go
├── matchNode.go
├── node.go
├── router.go
├── router_test.go
├── serverInterface.go
└── stringValue.go
0 directories, 13 filesPART1. Context是线程安全的吗?
显然不是.但是Context不需要被设计成线程安全的理由,和路由树不需要被设计成线程安全的理由不太一样.
路由树不需要被设计成线程安全,是因为按照我们的设计,当完成路由注册这个过程之后,WEB服务器才会被启动.相当于以WEB服务器启动这一事件为标记,在这个时刻之前,路由树被单线程写入;在这个时刻之后,路由树被多个goroutine读取.这意味着对于路由树而言,并没有并发读写的场景,因此根本不需要被设计成线程安全.
Context不需要保证线程安全,是因为按照我们的预期,这个Context只会被使用者在1个HandleFunc中使用,且不应该被多个goroutine操作(因为通常而言你并不会遇到很多个goroutine同时向http.ResponseWriter中写入的场景).
对于绝大多数人来说,他们不需要一个线程安全的Context.退一万步讲,如果真的需要一个线程安全的Context,那么提供一个装饰器,让用户使用前手动创建一个装饰器即可:
safeContext.go:
使用时手动创建即可:
context_test.go:
当然,站在框架设计者的角度来看,是不需要提供一个线程安全的Context的.
但是,这种装饰器的思路是很有用的.比如你新接触了一个框架,你预期他会给你一个线程安全的结构体,但是他没有做到线程安全.那么也可以用这种思路去封装他提供给你的、非线程安全的结构体.
PART2. Context为什么不设计为接口?
目前来看,看不出来设计为接口的必要性.
Echo框架将Context设计为接口,但是只有一个实现,就足以说明设计为接口有点过度设计的感觉.
即便Iris将Context设计为接口,且允许用户提供自定义实现,但是看起来也不是那么有用(因为没人提供自定义实现).
讲到这里,多提一嘴,之所以我们在设计HTTPServer时,设计了Server接口,是为了方便设计HTTPSServer
PART3. Context能不能用泛型?
我们已经在好几个地方用过泛型了(其实我在之前并没有用过,临场现补的.没用过的可以参考我整理的泛型初步笔记).在Context中,似乎也有使用泛型的场景.例如处理表单数据、查询参数、路径参数
一个比较常见的想法是:将Context.QueryValue()这种处理各部分输入的方法设计为泛型,这样直接可以返回用户所需的类型,如下:
理想很丰满现实很骨感,直接编译错误:

因为结构体方法不允许使用类型参数.
那么又有点子王想到,将StringValue做成泛型,实现如下:
那么我们考虑一下,在Context中创建StringValue时,该如何指定这个T的类型呢?将T指定为什么类型才是正确的呢?

答案是根本不知道.所以将StringValue做成泛型的方案也GG了.
PART4. 面试要点
4.1 能否重复读取HTTP协议的Body内容(即http.Request.Body能否被重复读取)?
http.Request.Body能否被重复读取)?原生API是不可以的.但是我们可以通过封装来允许重复读取.核心步骤是我们将http.Request.Body读取出来之后放到一个地方,后续都从这个地方读取即可.
4.2 能否修改HTTP协议的响应?
原生API也是不可以的.但是可以用我们的RespData这种机制,在最后再把数据刷新到网络中,在刷新之前,都可以修改
这里所谓的原生API,指的是http.ResponseWriter.Write()方法.很明显这个方法写完了就将响应体刷到前端去了,写完之后改不了.后边引入RespData(现在还没讲了)机制,就可以实现在刷到前端之前都是可以修改的.
4.3 Form 和 PostForm 的区别?
http.Request.Form与http.Request.PostForm的区别
正常的情况下你的API优先使用http.Request.Form就不太可能出错
4.4 Web框架是怎么支持路径参数的?
Web框架在发现匹配上了某个路径参数之后,将这段路径记录下来作为路径参数的值.这个值默认是string类型,用户自己可以转化为不同的类型
4.5 v5版本的实现
最终v5版本的实现如下(GoInAction/code/week2/context/v5):
Last updated