内容简介:在golang中实现一个简单的web服务很简单,代码如下:首先我们是通过ListenAndServe来监听本地端口的,之后ListenAndServe将收到的新建一个Response连同收到的Request作为参数调用ServeMux结构体的ServeHTTP(省略了中间过程).ServeHTTP将Request作为参数调用
在golang中实现一个简单的web服务很简单,代码如下:
package main import ( "net/http" "fmt" ) func main() { http.HandleFunc("/", hello) http.ListenAndServe(":9090", nil) } func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "hello world") }
首先我们是通过ListenAndServe来监听本地端口的,之后ListenAndServe将收到的新建一个Response连同收到的Request
作为参数调用ServeMux结构体的ServeHTTP(省略了中间过程).ServeHTTP将Request作为参数调用
Handler函数,Handler的返回值为一个Handler类型的接口,ServeHTTP会调用接口实现的ServeHTTP处理Response.
如果Request.URL.Path中有不合法的内容,则调用cleanPath清理,随后将Request.Host以及清理后的
内容传入handler函数,随后返回一个RedirectHandler以及handler所返回的路径。如果Request.URL.Path合法,那么
直接调用handler,返回值与handler返回值相同。
handler中通过判断ServeMux.hosts来决定是否实现pattern = r.Host + r.URL.Path.之后将pattern作为参数调用match,并将
match的返回值返回.
match的判别方式比较”有趣”,它虽然没实现为树形结构(只是用了映射),但是搜索的方法就是树形,因为URL路径就是个树形.它按照树的根节点
与子节点的关系进行判断,譬如路径”/home/select/usercourse”,match在匹配的时候会首先匹配到”/”(假如我们注册了),其次是”/home”,
之后逐层匹配下来,假如我们没注册过”/home/select/usercourse”,但是注册了”/home/select/”,那么match就会匹配到这一层.然后返回
“/home/select/”的Handler以及url(pattern).match函数的匹配规则实现在pathMatch
ServerMux结构体
type ServeMux struct { mu sync.RWMutex m map[string]muxEntry hosts bool // whether any patterns contain hostnames } type muxEntry struct { explicit bool h Handler pattern string }
NewServeMux()
DefaultServeMux变量就是直接调用这个函数,那么这个函数只make了一个变量m,也就是为map分配了空间,在golang的语法中,结构体里未申明的变量也是存在的(即分配了内存)
pathMath()
这个函数是ServeMux用来匹配路劲的主要函数,所以看一下策略还是很重要的,函数中pattern是我们注册的路劲,path是用户请求的路径
func pathMatch(pattern, path string) bool { if len(pattern) == 0 { // should not happen return false } n := len(pattern) if pattern[n-1] != '/' { return pattern == path } return len(path) >= n && path[0:n] == pattern }
如果我们挂在的路径不是以 /
结尾的,那么就直接判断两个参数是否相同,如果是以 /
结尾的,只要path的路径包含pattern那么久被判定是匹配,也就是说,如若我注册了 /home/select/
,那么 /home/select/hello
也会被定位到 /home/select/
上面,挂在的HanderFunc上面,这样做相当于为路径设置了一个index,不符合规则的URL都会被Redirct到这个index上。
* ServeMux.Handler()
注释写的很清楚,这个函数就是处理URL,然后调用*ServeMux.handler().首先调用cleanPath清理请求URL中的不合法内容。如果存在不合法内容,
则将清理过的URL交由*ServeMux.handler()处理并获得匹配到的pattern,然后修改url.Path的内容并调用RedirectHandler.
如果内容合法,则直接调用*ServeMux.handler()并返回结果
-
ServeMux.handler()
调用ServeMux.match()(封装了pathMatch函数)来获得匹配到的Handler以及对应pattern,如果ServeMux.hosts==true,那么
传入的参数为host + path,如果找不到的话,调用NotFoundHandler函数,并将其结果返回.
-
ServeMux.Handle()
Handle函数是用来注册路径与处理过程的.如果该路径已经存在了一个用户注册的Handler则会panic(意思就是说不支持覆盖).判别了合法参数以后就将
pattern作为key,新建一个muxEntry类型变量作为value加入到map中。
if pattern[0] != ‘/’ {
mux.hosts = true
}
这是这个函数中比较有意思的一个部分,通过这里我们可以看到如果注册路径的时候并不是以’/’开头的,那么ServeMux就会开启hosts,然后会在
请求到达的时候将URL.Host和URL.Path连接在一起放入match中寻找,具体信息请看这里
接下来是关于路径的处理,也就是关于”/home”与”/home/”的区别.我们先来看看作者怎么说
// Helpful behavior: // If pattern is /tree/, insert an implicit permanent redirect for /tree. // It can be overridden by an explicit registration.
如果路径的末尾是以’/’结尾并且该路径去掉末尾的’/’以后并没有被注册.那么将会去掉’/’并且为其绑定一个Redirect到现在的路径.
我自己写起来都觉得绕,举个例子就清楚了.
我注册了一个路径”/home/”,但是没有注册”/home”,那么如果用户访问了”/home”会发生什么呢?是的,会被Redirect到”/home/”.
需要注意的是,这里的muxEntry中的explicit没有填,也就是说是false,那么即是可以覆盖的.
-
ServeMux.ServeHTTP()
ServeHTTP会检测非法的URI(* )
如果通过检测就会调用自身的Handler()来返回注册的Handler,随后调用Handler的ServeHTTP方法
在http.ListenAndServe中的第二个参数传递处理hander可以自定义,这样就可以自己实现一个路由,在处理函数中必须实现ServeHTTP函数,这个函数在handler中直接执行。例子如下:
package routes import ( "encoding/json" "encoding/xml" "io/ioutil" "net/http" "net/url" "path/filepath" "regexp" "strconv" "strings" ) const ( CONNECT = "CONNECT" DELETE = "DELETE" GET = "GET" HEAD = "HEAD" OPTIONS = "OPTIONS" PATCH = "PATCH" POST = "POST" PUT = "PUT" TRACE = "TRACE" ) //commonly used mime-types const ( applicationJson = "application/json" applicationXml = "application/xml" textXml = "text/xml" ) type route struct { method string regex *regexp.Regexp params map[int]string handler http.HandlerFunc } type RouteMux struct { routes []*route filters []http.HandlerFunc } func New() *RouteMux { return &RouteMux{} } // Get adds a new Route for GET requests. func (m *RouteMux) Get(pattern string, handler http.HandlerFunc) { m.AddRoute(GET, pattern, handler) } // Put adds a new Route for PUT requests. func (m *RouteMux) Put(pattern string, handler http.HandlerFunc) { m.AddRoute(PUT, pattern, handler) } // Del adds a new Route for DELETE requests. func (m *RouteMux) Del(pattern string, handler http.HandlerFunc) { m.AddRoute(DELETE, pattern, handler) } // Patch adds a new Route for PATCH requests. func (m *RouteMux) Patch(pattern string, handler http.HandlerFunc) { m.AddRoute(PATCH, pattern, handler) } // Post adds a new Route for POST requests. func (m *RouteMux) Post(pattern string, handler http.HandlerFunc) { m.AddRoute(POST, pattern, handler) } // Adds a new Route for Static http requests. Serves // static files from the specified directory func (m *RouteMux) Static(pattern string, dir string) { //append a regex to the param to match everything // that comes after the prefix pattern = pattern + "(.+)" m.AddRoute(GET, pattern, func(w http.ResponseWriter, r *http.Request) { path := filepath.Clean(r.URL.Path) path = filepath.Join(dir, path) http.ServeFile(w, r, path) }) } // Adds a new Route to the Handler func (m *RouteMux) AddRoute(method string, pattern string, handler http.HandlerFunc) { //split the url into sections parts := strings.Split(pattern, "/") //find params that start with ":" //replace with regular expressions j := 0 params := make(map[int]string) for i, part := range parts { if strings.HasPrefix(part, ":") { expr := "([^/]+)" //a user may choose to override the defult expression // similar to expressjs: ‘/user/:id([0-9]+)’ if index := strings.Index(part, "("); index != -1 { expr = part[index:] part = part[:index] } params[j] = part parts[i] = expr j++ } } //recreate the url pattern, with parameters replaced //by regular expressions. then compile the regex pattern = strings.Join(parts, "/") regex, regexErr := regexp.Compile(pattern) if regexErr != nil { //TODO add error handling here to avoid panic panic(regexErr) return } //now create the Route route := &route{} route.method = method route.regex = regex route.handler = handler route.params = params //and finally append to the list of Routes m.routes = append(m.routes, route) } // Filter adds the middleware filter. func (m *RouteMux) Filter(filter http.HandlerFunc) { m.filters = append(m.filters, filter) } // FilterParam adds the middleware filter iff the REST URL parameter exists. func (m *RouteMux) FilterParam(param string, filter http.HandlerFunc) { if !strings.HasPrefix(param,":") { param = ":"+param } m.Filter(func(w http.ResponseWriter, r *http.Request) { p := r.URL.Query().Get(param) if len(p) > 0 { filter(w, r) } }) } // Required by http.Handler interface. This method is invoked by the // http server and will handle all page routing func (m *RouteMux) ServeHTTP(rw http.ResponseWriter, r *http.Request) { requestPath := r.URL.Path //wrap the response writer, in our custom interface w := &responseWriter{writer: rw} //find a matching Route for _, route := range m.routes { //if the methods don't match, skip this handler //i.e if request.Method is 'PUT' Route.Method must be 'PUT' if r.Method != route.method { continue } //check if Route pattern matches url if !route.regex.MatchString(requestPath) { continue } //get submatches (params) matches := route.regex.FindStringSubmatch(requestPath) //double check that the Route matches the URL pattern. if len(matches[0]) != len(requestPath) { continue } if len(route.params) > 0 { //add url parameters to the query param map values := r.URL.Query() for i, match := range matches[1:] { values.Add(route.params[i], match) } //reassemble query params and add to RawQuery r.URL.RawQuery = url.Values(values).Encode() + "&" + r.URL.RawQuery //r.URL.RawQuery = url.Values(values).Encode() } //execute middleware filters for _, filter := range m.filters { filter(w, r) if w.started { return } } //Invoke the request handler route.handler(w, r) break } //if no matches to url, throw a not found exception if w.started == false { http.NotFound(w, r) } } // ----------------------------------------------------------------------------- // Simple wrapper around a ResponseWriter // responseWriter is a wrapper for the http.ResponseWriter // to track if response was written to. It also allows us // to automatically set certain headers, such as Content-Type, // Access-Control-Allow-Origin, etc. type responseWriter struct { writer http.ResponseWriter started bool status int } // Header returns the header map that will be sent by WriteHeader. func (w *responseWriter) Header() http.Header { return w.writer.Header() } // Write writes the data to the connection as part of an HTTP reply, // and sets `started` to true func (w *responseWriter) Write(p []byte) (int, error) { w.started = true return w.writer.Write(p) } // WriteHeader sends an HTTP response header with status code, // and sets `started` to true func (w *responseWriter) WriteHeader(code int) { w.status = code w.started = true w.writer.WriteHeader(code) } // ----------------------------------------------------------------------------- // Below are helper functions to replace boilerplate // code that serializes resources and writes to the // http response. // ServeJson replies to the request with a JSON // representation of resource v. func ServeJson(w http.ResponseWriter, v interface{}) { content, err := json.MarshalIndent(v, "", " ") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Length", strconv.Itoa(len(content))) w.Header().Set("Content-Type", applicationJson) w.Write(content) } // ReadJson will parses the JSON-encoded data in the http // Request object and stores the result in the value // pointed to by v. func ReadJson(r *http.Request, v interface{}) error { body, err := ioutil.ReadAll(r.Body) r.Body.Close() if err != nil { return err } return json.Unmarshal(body, v) } // ServeXml replies to the request with an XML // representation of resource v. func ServeXml(w http.ResponseWriter, v interface{}) { content, err := xml.Marshal(v) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Length", strconv.Itoa(len(content))) w.Header().Set("Content-Type", "text/xml; charset=utf-8") w.Write(content) } // ReadXml will parses the XML-encoded data in the http // Request object and stores the result in the value // pointed to by v. func ReadXml(r *http.Request, v interface{}) error { body, err := ioutil.ReadAll(r.Body) r.Body.Close() if err != nil { return err } return xml.Unmarshal(body, v) } // ServeFormatted replies to the request with // a formatted representation of resource v, in the // format requested by the client specified in the // Accept header. func ServeFormatted(w http.ResponseWriter, r *http.Request, v interface{}) { accept := r.Header.Get("Accept") switch accept { case applicationJson: ServeJson(w, v) case applicationXml, textXml: ServeXml(w, v) default: ServeJson(w, v) } return }
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 原生JavaScript插件编写指南
- React Native 0.59.0 发布,使用 React 编写原生应用
- React Native 0.59.2 发布,使用 React 编写原生应用
- React Native 0.59.4 发布,使用 React 编写原生应用
- React Native 0.59.8 发布,使用 React 编写原生应用
- React Native 0.59.8 发布,使用 React 编写原生应用
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
微商团队管理实战手册
杜一凡 / 人民邮电出版社 / 2015-11 / 45.00元
回顾淘宝,用了10年时间才发展了不到1000万的卖家,再看微商,其仅一年时间就拥有了超过1000万的卖家。进入2015年,微商的发展之路虽有小坎坷,但前景依然被看好。然而任何一个想要做大、做强的微商都要以团队形式来发展,独立的个体只会举步维艰。 本书全面解读微商团队管理的营销书。全书共分为六章,分别从微商团队的商业秘密、微商团队的战略布局、管理基本功、建立高效团队、精通管理工具、未来发展等方......一起来看看 《微商团队管理实战手册》 这本书的介绍吧!