golang http.handler接口详解

转载 2016年06月01日 15:53:19

golang http.handler接口详解

1.标准库接口定义

package http

type Handler interface {
    ServeHTTP(w ResponseWriter, r *Request)
}

func ListenAndServe(address string, h Handler) error
ListenAndServe函数需要一个例如“localhost:8000”的服务器地址,和一个处理所有请求的Handler接口实例。它会一直运行,直到这个服务因为一个错误而失败(或者启动失败),它的返回值一定是一个非空的错误。

2.小Demo

type dollars float32
func (d dollars) String() string { return fmt.Sprintf("$%.2f", d) }

type MyHandler map[string]dollars
func (self MyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    switch req.URL.Path {
        case "/list":
            for item, price := range self {
                fmt.Fprintf(w, "%s: %s\n", item, price)
            }
        case "/price":
            item := req.URL.Query().Get("item")
            price, ok := self[item]
            if !ok {
                w.WriteHeader(http.StatusNotFound) // 404
                fmt.Fprintf(w, "no such item: %q\n", item)
                return
            }
            fmt.Fprintf(w, "%s\n", price)
        default:
            w.WriteHeader(http.StatusNotFound) // 404
            fmt.Fprintf(w, "no such page: %s\n", req.URL)
    }
}

func main() {
    handler := MyHandler{"shoes": 50, "socks": 5}
    log.Fatal(http.ListenAndServe("localhost:8000", handler))
}
现在handler基于URL的路径部分(req.URL.Path)来决定执行什么逻辑。如果这个handler不能识别这个路径,它会通过调用w.WriteHeader(http.StatusNotFound)返回客户端一个HTTP404错误
$ curl http://localhost:8000/list
shoes: $50.00
socks: $5.00
$ curl http://localhost:8000/price?item=socks
$5.00
$ curl http://localhost:8000/price?item=shoes
$50.00
$ curl http://localhost:8000/price?item=hat
no such item: "hat"
$ curl http://localhost:8000/help
no such page: /help
显然我们可以继续向ServeHTTP方法中添加case,但在一个实际的应用中,将每个case中的逻辑定义到一个分开的方法或函数中会很实用。对于更复杂的应用,一个ServeMux将一批http.Handler聚集到一个单一的http.Handler中,通过组合来处理更加错综复杂的路由需求。

3.ServeMux.Handle改进版

type MyHandler map[string]dollars
func (self MyHandler) list(w http.ResponseWriter, req *http.Request) {
    for item, price := range self {
        fmt.Fprintf(w, "%s: %s\n", item, price)
    }
}
func (self MyHandler) price(w http.ResponseWriter, req *http.Request) {
    item := req.URL.Query().Get("item")
    price, ok := self[item]
    if !ok {
        w.WriteHeader(http.StatusNotFound) // 404
        fmt.Fprintf(w, "no such item: %q\n", item)
        return
    }
    fmt.Fprintf(w, "%s\n", price)
}

func main() {
    handler := MyHandler{"shoes": 50, "socks": 5}
    mux := http.NewServeMux()
    mux.Handle("/list", http.HandlerFunc(handler.list))
    mux.Handle("/price", http.HandlerFunc(handler.price))
    log.Fatal(http.ListenAndServe("localhost:8000", mux))
}
语句http.HandlerFunc(handler.list)是一个转换而非一个函数调用,因为http.HandlerFunc是一个类型。它有如下的定义:
package http

type HandlerFunc func(w ResponseWriter, r *Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}
HandlerFunc显示了在Go语言接口机制中一些不同寻常的特点。这是一个有实现了接口http.Handler方法的函数类型。ServeHTTP方法的行为调用了它本身的函数。因此HandlerFunc是一个让函数值满足一个接口的适配器(此处是http.Handler接口适配器,因为实现了ServeHTTP方法),这里函数和这个接口仅有的方法有相同的函数签名。实际上,这个技巧让一个单一的类型例如MyHandler以多种方式满足http.Handler接口:一种通过它的list方法,一种通过它的price方法等等。

4.ServeMux.HandleFunc改进版

因为段落3中ServeMux.Handle方式注册非常普遍,ServeMux有一个方便的HandleFunc方法(ServeMux.Handle的该进),它帮我们简化handler注册代码成这样:
mux.HandleFunc("/list", handler.list)
mux.HandleFunc("/price", handler.price)
所以为了方便,net/http包提供了一个全局的ServeMux实例DefaultServerMux和包级别的http.Handle和http.HandleFunc函数。现在,为了使用DefaultServeMux作为服务器的主handler,我们不需要将它传给ListenAndServe函数;nil值就可以工作。
func main() {
    handler := MyHandler{"shoes": 50, "socks": 5}
    http.HandleFunc("/list", handler.list)
    http.HandleFunc("/price", handler.price)
    log.Fatal(http.ListenAndServe("localhost:8000", nil))
}
代码与段落3区别在于不手动创建ServeMux实例,而是使用net/http包的DefaultServerMux全局实例.

More:

基于并发考虑,web服务器应该在一个新的协程中调用每一个handler,所以当handler获取其它协程或者这个handler本身的其它请求也可以访问的变量时一定要使用预防措施比如锁机制。

本文源自《GO语言圣经》章节7.7. http.Handler接口

golang http.HandleFunc("/",func())每次都会匹配

利用golang的net/http库的函数: http.HandleFunc("/",func())进行注册,每次有http request的时候都会匹配“/"进行注册的函数。 原因不明!...
  • rufidmx
  • rufidmx
  • 2013年05月17日 14:27
  • 5753

Go-http-HandlerFunc()函数

前言在Go-HTTP中,已经介绍了HandlerFunc。为了查阅方便,单独再拎出来讲一讲。...

关于go语言中http做服务器使用正则的实例

package main import ( "net/http" "regexp" ) func main() { http.HandleFunc("/", route) http.Lis...

Go源码分析——http.ListenAndServe()是如何工作的

Go对web服务器的编写提供了非常好的支持,标准库中提供了net/http包来方便编写服务器。许多教程和书籍在讲到用Go编写web服务器时都会直接教新手用http包写一个最简单的hello world...

如何做一个简单的开放接口(4)-常见Handler的参考实现

1、概述核心引擎搞定了,接下来的主要工作就是逐个开发 Handler 了。常用的Handler包括授权(AuthHandler)、流量控制(TrafficControlHandler)、加解密(Enc...

go语言的http包

Go语言发展到现在,做web合适吗?与java php 相比,做网站方面有哪些优势、劣势?  Go语言现在作为生产环境系统的构建语言已经相当成熟了,很多知名互联网公司都在用了,比如:阿里、百度、...

使用Golang 搭建http web服务器

这篇文章出现的理由是业务上需要创建一个Web Server。创建web是所有语言出现必须实现的功能之一了。在nginx+fastcgi+php广为使用的今天,这里我们不妨使用Go来进行web服务器的搭...

golang-net/http源码分析之http server

golang-net/http源码分析之http server

Go语言 简单的http服务器示例

一个简单的http服务器代码 package main import ( "io" "net/http" "log" ) func HelloServe...

快速搭建HTTP服务器 go http server

go http server 例子,自定义路由。 package main import ( "fmt" "net/http" "reflect" "strings"...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:golang http.handler接口详解
举报原因:
原因补充:

(最多只允许输入30个字)