【轻知识】Go入门学习整理——第四节web开发,http请求处理流程、一个简单的名单录入。
栏目: JavaScript · 发布时间: 5年前
内容简介:关于web的开发知识,其实绕不过astaxie(beego作者)写的用http服务要引入net/http包。我们看下那个启动http服务的方法是哪个。
先从 hello world开始
关于web的开发知识,其实绕不过astaxie(beego作者)写的 《build-web-application-with-golang》 。我开发公司的beego项目之前看了一遍,现在再次拜读。
用http服务要引入net/http包。
我们看下那个启动http服务的方法是哪个。
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() }
ok ,接着看handler是什么鬼?
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
ResponseWriter呢?
type ResponseWriter interface { Header() Header Write([]byte) (int, error) WriteHeader(statusCode int) }
ok , 那也就是说有个结构体实现了ServeHTTP方法就可以传入到ListenAndServe中了,对吧?buddy!
package main import ( "fmt" "net/http" ) type HttpHandler struct { } // 回到的参数名名自己随便命名对吧。w也好response也好。只要按照参数的格式写就OK。http.ResponseWriter,当然就是package http中的ResponseWriter这个结构体。Request也一样。 func (h HttpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello world!") //这个写入到w的是输出到客户端的 } func main() { h := new(HttpHandler) http.ListenAndServe(":9090", h) }
get 请求 http://localhost:9090
,ok没问题是 hello world!
。
但,我们的接口通常都是好几个对吧。比如,我要添加数据,我要查数据。走不同的接口。也即是根据不同的Path做不同的事情。OK,没问题。
package main import ( "fmt" "net/http" ) type HttpHandler struct { } func search(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "select from db") } func add(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "insert into db") } func (h HttpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/search": search(w, r) case "/add": add(w, r) default: fmt.Fprintf(w, "Hello world!") } } func main() { h := new(HttpHandler) http.ListenAndServe(":9090", h) }
这样相当于我手动写了一个路由。那么有没有像 laravel 那样的路由方式呢?
Route::get('foo', function () { return 'Hello World'; });
http包里面有个HandleFunc函数可以实现。那把上面的代码改造下。
http.HandleFunc("/search", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "select from db") }) http.HandleFunc("/add", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "select from db") }) http.ListenAndServe(":9090", nil)
ok,这样分开之后看起来舒服多了。关于web服务的执行流程。astaxie写的文章可以琢磨下哈。
我是看明白了哈。简单的追下代码哈!主要是我想知道路由怎么分发的。
1.点入ListenAndServe
2.点入server.ListenAndServe()
3.点入 srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
4.点入go c.serve(ctx)(函数中找哈)(这行代码就是高性能的点,开启协程)
5.然后你会发现这行代码serverHandler{c.server}.ServeHTTP(w, w.req)(就是执行了我们的配置的handler)继续点入ServeHTTP
ok。重点来了,路由如何分发的。奥妙就在ServeHTTP中。
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler := sh.srv.Handler if handler == nil { handler = DefaultServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } handler.ServeHTTP(rw, req) }
回顾下,我们上来先自定义的那个结构体了么?然后传给了ListenAndServe。实际上server保存了它。于是,在ServeHTTP方法中的体现就是这行代码 handler := sh.srv.Handler
。因为我传了所以不为nil。于是走handler.ServeHTTP(rw, req)。执行了我自定义的HttpHandler中的ServeHTTP。
OK,那么 handler == nil ,也就是handler为空呢?也就是我们用HandleFunc改造过的写法。ListenAndServe里面穿的nil。也就意味着server保存的handler为nil。于是在ServeHTTP方法中走这里 handler = DefaultServeMux
。
这里再提两点。剩下的自己追下代码。
1.HandleFunc 作用就是给DefaultServeMux配置了路由。保存到了map中。 2.DefaultServeMux 类型是*ServeMux。ServeMux里面实现了ServeHTTP(想起来了么,只要实现了ServeHTTP就能处理http请求与响应),实现的ServeHTTP方法里面根据path做了路由转发,也就是从之前保存的map里面拿出来对应的handler。最后执行。
来了?表单!
OK,接下来要做的就是,准备好简单的前端页面。一个框框(输入名字)一个按钮(提交),一个列表(成功后刷新列表)。姑且就叫做名单功能吧。
接着之前的代码哈。已经有了add方法(添加名单),search方法(展示列表)。当然还没有实现。还需要一个display方法(渲染页面)。三个方法就OK了。
功能
image.png
1.包级作用域的nameList数组,存放名单。
2.页面加载请求search方法(也就是列表),添加把name添加到nameList中。
目录结构
─web └───views └─── list.html └───main.go
贴代码
先准备页面,js用vue。贴代码。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>名单列表</title> <script type="text/javascript" src="https://unpkg.com/vue@2.1.10/dist/vue.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue-resource@1.3.4"></script> </head> <body> <div id="app"> <div> <input type="text" placeholder="name" v-model="name"> <button @click="add()">add</button> </div> <div> <ul> <li v-for="value in nameList"> <p>ID: {{value.id}}</p> <p>Name: {{value.name}}</p> </li> </ul> </div> </div> <script> Vue.http.options.emulateJSON = true; Vue.http.options.headers = { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' }; //Vue-resource中post请求将data数据以request payload转换为form data的形式 var app = new Vue({ el: '#app', mounted: function() { let that = this this.$http.post("/search", {}).then(function(res) { that.nameList = JSON.parse(res.bodyText).name_list }) }, data: { name:'', nameList: [] }, methods: { add: function() { let that = this // 发送请求给 后台 this.$http.post("/add", {name: this.name}).then(function(res) { this.$http.post("/search", {}).then(function(res) { that.nameList = JSON.parse(res.bodyText).name_list }) }) } } }); </script> </body> </html>
贴go代码
package main import ( "encoding/json" "fmt" "html/template" "net/http" ) var nameList []map[string]interface{} func main() { http.HandleFunc("/list", func(w http.ResponseWriter, r *http.Request) { pageFile := "./views/list.html" t, error := template.New("list.html").Delims("<<<", ">>>").ParseFiles(pageFile) if error != nil { http.Error(w, error.Error(), http.StatusInternalServerError) } t.Execute(w, nil) }) http.HandleFunc("/search", func(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{"name_list":nameList} list, err := json.Marshal(data) if err != nil { fmt.Println("json.Marshal failed:", err) return } w.Header().Add("Content-Type","application/json") fmt.Fprintf(w, string(list)) //这个写入到w的是输出到客户端的 }) http.HandleFunc("/add", func(w http.ResponseWriter, r *http.Request) { fmt.Println(r.FormValue("name")) fmt.Println(r.Method) name := r.FormValue("name") if name == "" { fmt.Fprintf(w, "don't ") return } var nameMap map[string]interface{} = map[string]interface{}{"id": len(nameList) + 1, "name": name} nameList = append(nameList, nameMap) // evaluated but not used 这种报错是需要接收返回值 fmt.Println(nameList) fmt.Fprintf(w, "add from db") }) http.ListenAndServe(":9090", nil) }
总结,关于这个程序呢。有些不足之处,比如校验没做,比如错误处理没做。比如没有统一返回的函数与格式。对吧!但先到这里。
下一步要做的就是操作 mysql 或者再加redis。而不是放到包级作用域的数组中。
参考资料:
*《build-web-application-with-golang》 https://github.com/astaxie/build-web-application-with-golang
*《Go语言实战笔记(二十)| Go Context》 https://www.flysnow.org/2017/05/12/go-in-action-go-context.html
*《Change default delimiters templating with “template.Delims”》 https://medium.com/@etiennerouzeaud/change-default-delimiters-templating-with-template-delims-857938a0b661
*《Vue-resource中post请求将data数据以request payload转换为form data的形式》 https://blog.csdn.net/qq_35844177/article/details/70170416
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 用深度学习优化OCR,「睿琪」百倍提升票据录入效率
- 辽宁省“找厕所”平台正式上线,已录入7000余座厕所信息
- TiDB入门(四):从入门到“跑路”
- MyBatis从入门到精通(一):MyBatis入门
- MyBatis从入门到精通(一):MyBatis入门
- Docker入门(一)用hello world入门docker
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。