Open geektutu opened 5 years ago
赞一下,非常6
正确的调用顺序应该是Header().Set 然后WriteHeader() 最后是Write()
@walkmiao 正确的调用顺序应该是Header().Set 然后WriteHeader() 最后是Write()
感谢指出,在 WriteHeader()
后调用 Header().Set
是不会生效的,已经更正~
@geektutu
@walkmiao 正确的调用顺序应该是Header().Set 然后WriteHeader() 最后是Write()
感谢指出,在
WriteHeader()
后调用Header().Set
是不会生效的,已经更正~
感谢分享的教程
大佬 JSON()这里有点问题
func (c *Context) JSON(code int, obj interface{}) {
c.SetHeader("Content-Type", "application/json")
c.Status(code)
// c.StatusCode = code
// c.Writer.WriteHeader(code)
encoder := json.NewEncoder(c.Writer)
if err := encoder.Encode(obj); err != nil {
http.Error(c.Writer, err.Error(), 500)
// w.Header().Set("Content-Type", "text/plain; charset=utf-8")
// w.Header().Set("X-Content-Type-Options", "nosniff")
// w.WriteHeader(code)
// fmt.Fprintln(w, error)
}
}
如果err!=nil的话http.Error(c.Writer, err.Error(), 500)这里是不起作用的,因为前面已经执行了WriteHeader(code),那么返回码将不会再更改http.Error(c.Writer, err.Error(), 500)里面的w.WriteHeader(code)、w.Header().Set()不起作用,而且encoder.Encode(obj)相当于调用了Write(),http.Error(c.Writer, err.Error(), 500)里面的WriteHeader、Header().Set()操作都是无效的。我看了gin的代码,如果encoder.Encode(obj)这里报错的话是直接panic,感觉这里如果err!=nil的话确实不好处理
附上gin的代码 render/json.go 56行
// Render (JSON) writes data with custom ContentType.
func (r JSON) Render(w http.ResponseWriter) (err error) {
if err = WriteJSON(w, r.Data); err != nil {
panic(err)
}
return
}
// WriteContentType (JSON) writes JSON ContentType.
func (r JSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
// WriteJSON marshals the given interface object and writes it with custom ContentType.
func WriteJSON(w http.ResponseWriter, obj interface{}) error {
writeContentType(w, jsonContentType)
encoder := json.NewEncoder(w)
err := encoder.Encode(&obj)
return err
}
@xiaoxfan 非常感谢指出了这个问题,之前的实现没有注意到这个问题。看到这里的童鞋可以看看gin的实现,地址贴在这里render/json.go#L56
day2-context/gee/router.go文件的第18行的正确写法是handler(c.Writer, c.Req),在handle函数中
感谢教程!我在使用curl命令 curl "http://localhost:9999/login" -X POST -d 'username=geektutu&password=1234' 时无法解析出传递的参数,并且显示'password' 不是内部或外部命令,也不是可运行的程序或批处理文件。这是什么原因?
@lorenwe day2-context/gee/router.go文件的第18行的正确写法是handler(c.Writer, c.Req),在handle函数中
文章的没错, 你是不是 gee.go中 type HandlerFunc func(*Context)
没改, 还是写的type HandlerFunc func(w http.ResponseWriter, r *http.Request)
?
感谢手把手的教我框架的设计思想 让我对人生又重新燃起了希望
兔兔,兔兔我爱你,我要给你生🐒(虽然我是男的)
@Pissssofshit 这。。。公司联谊你可以的。😉
@Pissssofshit 这。。。公司联谊你可以的。😉
嘿嘿嘿,gay里gay气
@Leoooo-tqp 感谢教程!我在使用curl命令 curl "http://localhost:9999/login" -X POST -d 'username=geektutu&password=1234' 时无法解析出传递的参数,并且显示'password' 不是内部或外部命令,也不是可运行的程序或批处理文件。这是什么原因?
用双引号
非常棒,手敲一遍,感触颇深
@walkmiao
@geektutu
@walkmiao 正确的调用顺序应该是Header().Set 然后WriteHeader() 最后是Write()
感谢指出,在
WriteHeader()
后调用Header().Set
是不会生效的,已经更正~感谢分享的教程
这个调用顺序是说哪里啊
@Leoooo-tqp 感谢教程!我在使用curl命令 curl "http://localhost:9999/login" -X POST -d 'username=geektutu&password=1234' 时无法解析出传递的参数,并且显示'password' 不是内部或外部命令,也不是可运行的程序或批处理文件。这是什么原因?
正确调用姿势:curl --location --request POST '127.0.0.1:9999/login' \ --form 'password="张三"' \ --form 'username="李思"'
感谢分享~
感谢分享~
@DiDiDaDiDiDa 感谢认可,笔芯~ 😄
@Leoooo-tqp 感谢教程!我在使用curl命令 curl "http://localhost:9999/login" -X POST -d 'username=geektutu&password=1234' 时无法解析出传递的参数,并且显示'password' 不是内部或外部命令,也不是可运行的程序或批处理文件。这是什么原因?
我也有这个,难道是命令不一致吗
@jianhuacc
@Leoooo-tqp 感谢教程!我在使用curl命令 curl "http://localhost:9999/login" -X POST -d 'username=geektutu&password=1234' 时无法解析出传递的参数,并且显示'password' 不是内部或外部命令,也不是可运行的程序或批处理文件。这是什么原因?
我也有这个,难道是命令不一致吗
用双引号,curl "http://localhost:9999/login" -X POST -d "username=geektutu&password=1234"
@HongTian
@jianhuacc
@Leoooo-tqp 感谢教程!我在使用curl命令 curl "http://localhost:9999/login" -X POST -d 'username=geektutu&password=1234' 时无法解析出传递的参数,并且显示'password' 不是内部或外部命令,也不是可运行的程序或批处理文件。这是什么原因?
我也有这个,难道是命令不一致吗
用双引号,curl "http://localhost:9999/login" -X POST -d "username=geektutu&password=1234"
用双引号完美解决,赞!!!
很棒的教程~
@Leoooo-tqp 感谢教程!我在使用curl命令 curl "http://localhost:9999/login" -X POST -d 'username=geektutu&password=1234' 时无法解析出传递的参数,并且显示'password' 不是内部或外部命令,也不是可运行的程序或批处理文件。这是什么原因?
改成双引号即可
讲的太好了
@codevvvv9
@walkmiao
@geektutu
@walkmiao 正确的调用顺序应该是Header().Set 然后WriteHeader() 最后是Write()
感谢指出,在
WriteHeader()
后调用Header().Set
是不会生效的,已经更正~感谢分享的教程
这个调用顺序是说哪里啊
If
WriteHeader
has not yet been called,Write
callsWriteHeader(http.StatusOK)
before writing the data.
Superfluous WriteHeader
has no effect. That's mean if we want to set
status code except http.StatusOK, we should call WriteHeader
before Write
.
Changing the header map after a call to WriteHeader
(or Write
) has no effect
unless the modified headers are trailers. That's mean if we want to modify the response
header, we should call Writer.Header().Set(key, value)
before WriteHeader
.
func (c *Context) Render(code int, r render.Render) {
c.Status(code)
if !bodyAllowedForStatus(code) {
r.WriteContentType(c.Writer)
c.Writer.WriteHeaderNow()
return
}
if err := r.Render(c.Writer); err != nil {
panic(err)
}
}
我发现gin中的*Context.JSON
方法会调用*Context.Render
方法,而此方法会最先调用WriteHeader
方法,在整个调用链最后才会设置Content-Type
func writeContentType(w http.ResponseWriter, value []string) {
header := w.Header()
if val := header["Content-Type"]; len(val) == 0 {
header["Content-Type"] = value
}
}
这是为什么呢?不应该是 Header().Set 然后WriteHeader() 最后是Write()吗?
明白了 gin中的c.Writer类型是gin.ResponseWriter, 实际类型是gin.responseWriter,不是http.ResponseWriter
@geektutu @xiaoxfan 非常感谢指出了这个问题,之前的实现没有注意到这个问题。看到这里的童鞋可以看看gin的实现,地址贴在这里render/json.go#L56
Req.FormValue(key) 获取不到post参数吗
66
没有json输出
@Juminiy 没有json输出
没有问题的
如果能进行错误处理就更好了
感觉json函数可以写成这样,就不用担心状态码写不进去了 c.SetHeader("Content-Type", "application/json") buffer := new(bytes.Buffer) encode := json.NewEncoder(buffer) if err := encode.Encode(obj); err != nil { http.Error(c.Writer, err.Error(), 500) } c.Status(code) c.Writer.Write(buffer.Bytes())
打卡第二天~
感谢大佬的分享!小白有个小疑问,最后一段那里,engine类型应该是通过实现了ServerHTTP方法实现了http.Handler接口,而不是实现了ServeHTTP 接口。感觉这里应该是笔误?
大佬,你好,请问一下,http.ResponseWriter.wirte()这个write方法的实现在哪里查看呢,我在goland中只能跳转到ResponseWriter的接口定义,怎么可以找到实现的方法吗?
GoLand 可以看接口实现有哪些。
如果太多的话可以打断点跑跑看。
day2-context git:(master) curl -i http://localhost:9999/
HTTP/1.1 200 OK
Date: Thu, 10 Mar 2022 12:19:00 GMT
Content-Length: 15
Content-Type: text/plain; charset=utf-8
URL.Path = "/"
想问一下为什么我将代码下载到本地,终端输入这个之后他一直回复URL.Path = "/",萌新不知道是什么原因....
@ZhuoxueQAQ 感谢大佬的分享!小白有个小疑问,最后一段那里,engine类型应该是通过实现了ServerHTTP方法实现了http.Handler接口,而不是实现了ServeHTTP 接口。感觉这里应该是笔误?
没错吧 type Handler interface { ServeHTTP(ResponseWriter, *Request) }
这种只能获取到 Content-Type: application/x-www-form-urlencoded 的数据吧
太棒了呀,太棒了呀,谢谢大佬带我感受技术的魅力
@EndlessCheng
大佬,你好,请问一下,http.ResponseWriter.wirte()这个write方法的实现在哪里查看呢,我在goland中只能跳转到ResponseWriter的接口定义,怎么可以找到实现的方法吗?
GoLand 可以看接口实现有哪些。
如果太多的话可以打断点跑跑看。
零神居然也在看这个😂
每个单独小块基本弄明白了,但整个流程在脑海中就是没法顺利跑通。看来说到底还是没搞懂,学海无涯,小白膜拜大佬。
太妙了太妙了,写轮子真的妙哇。精彩绝伦!
2023年命令修改:curl "http://localhost:9999/login" -Method POST -Body 'username=geektutu&password=1234' 而且解码json的方式最新的gin也变了: jsonBytes, err := json.Marshal(obj) _, err = c.Writer.Write(jsonBytes)
@harmonic-lqw 2023年命令修改:curl "http://localhost:9999/login" -Method POST -Body 'username=geektutu&password=1234' 而且解码json的方式最新的gin也变了: jsonBytes, err := json.Marshal(obj) _, err = c.Writer.Write(jsonBytes)
http的post方法发送body参数的时候有好几种格式呢,你这个是键值对。
例如可以参考的Content-Type
就有application/json
,application/x-www-form-urlencoded
等
@feymanpaper 太妙了太妙了,写轮子真的妙哇。精彩绝伦!
感谢geektutu
哈哈哈,跟着写轮子的同时也把golang
的代码思想捋清了一遍。截止到目前为止,golang
赛高!!
最后的 main 函数执行结果,第一个应该只有 html 返回,没有其余的 response header
吧
$ curl -i http://localhost:9999/
<h1>Hello Gee</h1>
来学习了
https://geektutu.com/post/gee-day2.html
7天用 Go语言 从零实现Web框架教程(7 days implement golang web framework from scratch tutorial),用 Go语言/golang 动手写Web框架,从零实现一个Web框架,从零设计一个Web框架。本文介绍了请求上下文(Context)的设计理念,封装了返回JSON/String/Data/HTML等类型响应的方法。