标准库net/http包的基本使用
标准库net/http包的基本使用
【参考文献】一文读懂Go语言的net/http标准库 - 掘金 (juejin.cn)
为什么要了解这个库
我们习惯在Web开发时使用Web应用框架,这可以让我们的开发变得更加迅速和简便,这也意味着开发者必须接受框架自身的一套约定和模式。虽然很多框架都认为自己提供的约定和模式是最佳实践(best practice),但是如果开发者没有正确地理解这些最佳实践,那么对最佳实践的应用就可能会发展为货物崇拜编程(cargo cult programming):开发者如果不了解这些约定和模式的用法,就可能会在不必要甚至有害的情况下盲目地使用它们。
第二次世界大战期间,盟军为了对战事提供支援,在太平洋的多个岛屿上设立了空军基地,以空投的方式向部队以及支援部队的岛民投送了大量生活用品以及军事设备,从而极大地改善了部队以及岛民的生活,岛民也因此第一次看到了人工生产的衣物、罐头食品以及其他物品。在战争结束之后,这些空军基地便被废弃了,货物空投自然也停止了。此时,岛民做了一件非常符合其本性的事情——他们把自己打扮成空管员、士兵以及水手,使用机场上的指挥棒挥舞着着陆信号,进行地面阅兵演习,试图让飞机继续空投货物,货物崇拜一词也因此而诞生。
尽管货物崇拜程序员并没有像岛民一样挥舞指挥棒,但他们却大量地复制和粘贴从StackOverflow这类网站上找来的代码,这些代码虽然能够运行,但是他们却对这些代码的工作原理一点也不了解。这样做的结果是,他们通常无法扩展和修改这些代码。与此类似,货物崇拜程序员通常会在既不了解框架为什么使用特定的模式或约定,也不知道框架做了何种取舍的情况下,盲目地使用Web框架。
举个例子来说,因为HTTP是一种无连接协议(connection-less protocol),通过这种协议发送给服务器的请求对服务器之前处理过的请求一无所知,所以应用程序才会以cookie的方式在客户端实现数据持久化,并以会话的方式在服务器上实现数据持久化,而不了解这一点的人是很难理解为什么要在不同连接之间使用cookie和会话实现信息持久化的。
为了降低使用cookie和会话带来的复杂性,Web应用框架通常都会提供一个统一的接口(uniform interface),用于在连接之间实现持久化。这样做的结果是,很多新手程序员都会想当然地假设在连接之间进行持久化唯一要做的就是使用框架提供的接口。但是由于这类接口通常都是根据框架自身的习惯指定的,因此不同框架提供的接口可能会有所不同。更糟糕的是,不同的框架可能会提供一些名字相同的接口,但是这些同名接口之间的实现却又千差万别、各不相同,因此给开发者带来不必要的困惑。
通过这个例子可以看出,使用框架进行Web应用开发意味着将框架与应用进行绑定,之后无论是将应用迁移到另一个框架,还是对应用进行扩展,又或者为应用添加新的特性,都需要对框架本身有深入的了解,在某些情况下可能还需要对框架进行定制。
本文的目的并不是让大家抛弃框架、约定和模式——一个好的框架通常是快速构建可扩展且健壮的Web应用的最好方法,但理解那些隐藏在框架之下的底层概念和基础设施也是非常重要的。只要对框架的实现原理有了正确的认识,我们就可以更加清晰地了解到这些约定和模式是如何形成的,从而避免陷阱、理清思路,不再盲目地使用模式。
对于Go来说,隐藏在框架之下的通常是net/http和html/template这两个标准库,如下图所示,net/http标准库可以分为客户端和服务器两个部分,库中的结构和函数有些只支持客户端和服务器这两者中的一个,而有些则同时支持客户端和服务器:
Client
、Response
、Header
、Request
和Cookie
对客户端进行支持;Server
、ServeMux
、Handler/HandleFunc
、ResponseWriter
、Header
、Request
和Cookie
则对服务器进行支持。
本文接下来将会展示如何把net/http标准库用作服务器以及如何使用Go语言接收客户端发送的HTTP请求。
HTTP协议
超文本传输协议(Hypertext Transfer Protocol,HTTP协议)是今天使用最广泛的应用层协议,1989年由Tim Berners-Lee在CERN起草的协议已经成为了互联网的数据传输的核心。目前HTTP/2和HTTP/3也对现有的协议进行了更新,提供更加安全和快速的传输功能。多数的编程语言都会在标准库中实现HTTP/1.1和HTTP/2.0,基本满足工程师的日常开发需求。
HTTP协议是应用层协议,在通常情况下我们都会使用TCP作为底层的传输层协议传输数据包,但是在HTTP/3在UDP协议上实现了心得传输层协议QUIC并使用QUIC传输数据,这也意味着HTTP既可以跑在TCP上,也可以跑在UDP上。
Go Web服务器
在Go Web编程中,我们主要关注的是如何使用net/http标准库的服务器功能而非客户端功能。
如图所示为通过Go服务器处理请求,通过net/http
标准库,我们可以启动一个HTTP服务器,然后让这个服务器接受请求并向请求返回响应。除此之外,net/http
标准库还提供了一个连接多路复用器(multiplexer)的接口以及一个默认的多路复用器。
跟其他编程语言里面的绝大多数标准库不一样,Go提供了一系列用于创建Web服务器的标准库。如代码下面代码所示,创建一个服务器的步骤非常简单,只要调用ListenAndServe
并传入网络地址以及负责处理请求的处理器(handler)作为参数就可以了。如果网络地址参数为空字符串,那么服务器默认使用80端口进行网络连接;如果处理器参数为nil
,那么服务器将使用默认的多路复用器DefaultServeMux。
//最简单的Web服务器
package main
import (
"net/http"
)
func main() {
http.ListenAndServe("", nil)
}
用户除了可以通过ListenAndServe
的参数对服务器的网络地址和处理器进行配置之外,还可以通过Server
结构对服务器进行更详细的配置,其中包括为请求读取操作设置超时时间、为响应写入操作设置超时时间以及为Server
结构设置错误日志记录器等。
下面的代码和上面的代码作用基本是相同的,它们之间唯一区别在于下面代码可以通过Server
结构对服务器进行更多的配置。
//带有附加配置的Web服务器
package main
import (
"net/http"
)
func main() {
server := http.Server{
Addr: "127.0.0.1:8080",
Handler: nil,
}
server.ListenAndServe()
}
下面展示了Server
结构体所有可选的配置选项:
type Server struct {
Addr string
Handler Handler
ReadTimeout time.Duration
WriteTimeout time.Duration
MaxHeaderBytes int
TLSConfig *tls.Config
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
ConnState func(net.Conn, ConnState)
ErrorLog *log.Logger
}
通过HTTPS提供服务
当客户端和服务器需要共享密码或者信用卡信息这样的私密信息时,大多数网站都会使用HTTPS对客户端和服务器之间的通信进行加密和保护。在一些情况下,这种保护甚至是强制性的。比如说,如果一个网站提供了信用卡支付功能,那么按照支付卡行业数据安全标准(Payment Card Industry Data Security Standard),这个网站就必须对客户端和服务器之间的通信进行加密。像Gmail和Facebook这样带有隐私性质的网站甚至在整个网站上都启用了HTTPS。如果你打算开发一个网站,而这个网站又需要提供用户登录功能,那么你也需要在这个网站上启用HTTPS。
HTTPS实际上就是将HTTP通信放在SSL之上进行。通过使用ListenAndServeTLS
函数,我们可以让之前展示过的简单Web应用也提供HTTPS服务,如下面代码:
//通过HTTPS提供服务
package main
import (
"net/http"
)
func main() {
server := http.Server{
Addr: "127.0.0.1:8080",
Handler: nil,
}
server.ListenAndServeTLS("cert.pem", "key.pem")
}
这段代码中的cert.pem
文件时SSL证书,而key.pem
则是服务器的私钥(private key)。在生产环境中使用的SSL证书需要通过VeriSign、Thawte或者Comodo SSL这样的CA取得,但如果是出于测试目的才使用证书和私钥,那么使用自行生成的证书就可以了。生成证书的办法有很多,其中一种就是使用Go标准库中的crypto
包群(library group)。
SSL(Secure Socket Layer,安全套接字层)是一种通过公钥基础设施(Public Key Infrastructure,PKI)为通信双方提供数据加密和身份验证的协议,其中通信的双方通常是客户端和服务器。SSL最初由Netscape公司开发,之后由IETF(Internet Engineering Task Force,互联网工程任务组)接手并将其改名为TLS(Transport Layer Security,传输层安全协议)。HTTPS,即SSL之上的HTTP,实际上就是在SSL/TLS连接的上层进行HTTP通信。
HTTPS需要使用SSL/TLS证书来实现数据加密以及身份验证。SSL证书存储在服务器之上,它是一种使用X.509格式进行格式化的数据,这些数据包含了公钥以及其他一些相关信息。为了保证证书的可靠性,证书一般由证书分发机构(Certificate Authority,CA)签发。服务器在接收到客户端发送的请求之后,会将证书和响应一并返回给客户端,而客户端在确认证书的真实性之后,就会生成一个随机密钥(random key),并使用证书中的公钥对随机密钥进行加密,此次加密产生的对称密钥(symmetric key)就是客户端和服务器在进行通信时,负责对通信实施加密的实际密钥(actual key)。客户端将加密的对称密钥送回服务器,由于该对称密钥是使用服务器的公钥加密的,因此只有拥有响应私钥的服务器可以解密它。服务器使用自己的私钥对加密的对称密钥进行解密,恢复出最初由客户端生成的随机密钥。现在,客户端和服务器都拥有相同的对称密钥,可以使用对称密钥加密算法对通信进行加密和解密。双方可以使用该对称密钥来保护数据的机密性和完整性,以确保在通信过程中的安全性。
虽然我们不会在生产环境中使用自行生成的证书和私钥,但了解SSL证书和私钥的生成方法,并学会在开发和测试中使用证书和私钥,也是一件很有意义的事。下面代码展示生成SSL证书和服务器私钥的代码。
这个过程的步骤并不是特别复杂。因为SSL证书实际上就是一个将扩展密钥用法(extended key usage)设置成了服务器身份验证操作的X.509证书,所以程序在生成证书时使用了crypto/x509
标准库。此外,因为创建证书需要用到私钥,所以程序在使用私钥成功创建证书之后,会将私钥单独保存在同一个存放服务器私钥的文件里面。
//生成个人使用的SSL证书以及服务器私钥
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"net"
"os"
"time"
)
func main() {
max := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, _ := rand.Int(rand.Reader, max)
subject := pkix.Name{
Organization: []string{"Manning Publications Co."},
OrganizationalUnit: []string{"Books"},
CommonName: "Go Web Programming",
}
//程序使用一个Certificate结构来对证书进行配置
template := x509.Certificate{
//证书序列号,用于记录由CA分发的唯一号码,为了能让我们的Web应用运行起来,
//程序在这里生成了一个非常长的随机整数来作为证书序列号。
SerialNumber: serialNumber,
//程序创建了一个专有名称(distinguished name),并将它设置成了证书
//的标题(subject)
Subject: subject,
//程序还将证书的有限期设置成一年
NotBefore: time.Now(),
NotAfter: time.Now().Add(365 * 24 * time.Hour),
//下面两个字段的值表明了这个X.509证书是用于进行服务器身份验证操作的。
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
//最后,程序将证书设置成了只能在IP地址127.0.0.1之上运行
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
}
//程序通过调用crypto/rsa标准库中的GenerateKey函数
pk, _ := rsa.GenerateKey(rand.Reader, 2048)
//程序创建的RSA私钥的结构里面包含了一个能够公开访问的公钥(public key),
//这个公钥在使用x509.CreateCertificate函数创建SSL证书的时候就会用到:
derBytes, _ := x509.CreateCertificate(rand.Reader, &template,
&template, &pk.PublicKey, pk)
//CreateCertificate函数接受Certificate结构、公钥和私钥等多个参数,
//创建出一个经过DER编码格式化的字节切片。后续代码的意图也非常简单明了,
//它们首先使用encoding/pem标准库将证书编码到cert.pem文件里面:
certOut, _ := os.Create("cert.pem")
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
certOut.Close()
//然后继续以PEM编码的方式把之前生成的密钥编码并保存到key.pem文件里面:
keyOut, _ := os.Create("key.pem")
pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes:
x509.MarshalPKCS1PrivateKey(pk)})
keyOut.Close()
}
最后需要提醒的是,如果证书是由CA签发的,那么证书文件中将同时包含服务器签名以及CA签名,其中服务器签名在前,CA签名在后。
X.509是国际电信联盟电信标准化部门(ITU-T)为公钥基础设施制定的一个标准,这个标准包含了公钥证书的标准格式。
一个X.509证书(简称SSL证书)实际上就是一个经过编码的ASN.1(Abstract Syntax Notation One,抽象语法表示法/1)格式的电子文档。ASN.1既是一个标准,也是一种表示法,它描述了表示电信以及计算机网络数据的规则和结构。
X.509证书可以使用多种格式编码,其中一种编码格式是BER(Basic Encoding Rules,基本编码规则)。BER格式指定了一种自解释并且自定义的格式用于对ASN.1数据结构进行编码,而DER格式则是BER的一个子集。DER只提供了一种编码ASN.1值的方法,这种方法被广泛地应用于密码学当中,尤其是对X.509证书进行加密。
SSL证书可以以多种不同的格式保存,其中一种是PEM(Privacy Enhanced Email,隐私增强邮件)格式,这种格式会对DER格式的X.509证书实施Base64编码,并且这种格式的文件都以
-----BEGIN CERTIFICATE-----
开头,以-----END CERTIFICATE-----
结尾(除了用作文件格式之外,PEM和此处讨论的SSL证书关系并不大)。
http 包中如何通过 url 路径来处理业务逻辑
package main
import (
"fmt"
"net/http"
)
func handlerFunc(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "<h1>Hello, 这里是 goblog</h1>")
fmt.Fprint(w, "请求路径为:"+r.URL.Path)
}
func main() {
http.HandleFunc("/", handlerFunc)
http.ListenAndServe(":3000", nil)
}
路径解析的代码是:
http.HandleFunc("/", handlerFunc)
初学者很容易误以为这段代码中正斜杠/
是站点的根目录,其实不然,它意味着任意路径。可以利用此机制来设置多页面访问。
使用 air 来自动重载代码
1、自动重载方案,比较老牌的是fresh,不过此项目已经放弃维护,这里使用air,先进行安装:
$ go install github.com/cosmtrek/air@latest
下载后air的可执行文件被存储在GOPATH/bin下,所以记得GOPATH/bin添加到PATH的环境变量中。也可以从链接github.com/cosmtrek/air/releases下载air.exe放入这个目录下。
2、安装成功后使用以下命令检查下:
$ air -v
__ _ ___
/ /\ | | | |_)
/_/--\ |_| |_| \_ , built with Go
3、使用air,在项目根目录下运行下面命令:
$ air
执行结果如图:
修改后就可以自动重载了。这个时候根目录下会出现一个tmp
文件夹(git status也可以看到),这个是air命令的编译文件存放地,我们需要设置版本控制器将其排除在外:
.gitignore:
tmp
执行git status可以看到tmp
目录已被排除在外。
如何添加 http 标头
我们将为返回内容设置正确的标头,以解决 about 页面 HTML 无法正确解析的问题。
HTTP报文格式和标头
【HTTP请求报文格式以及响应报文格式】HTTP请求报文主要由请求行、请求头部、请求正文3部分组成。HTTP响应报文主要由状态行、响应头部、响应正文3部分组成。
在请求头部或响应头部中存放的是HTTP标头,HTTP 标头是用于 HTTP 请求或响应的字段,它传递关于请求或者响应的额外上下文和元数据。例如,请求消息可以使用标头表明它首选的媒体格式,而响应可以使用标头表明返回主体的媒体格式。标头是不区分大小写,开始于行首,后面紧跟着一个 ':'
和与之相关的值。字段值在一个换行符(CRLF)前或者整个消息的末尾结束。
HTTP1.1标头分为四类:通用标头、请求标头、响应标头和实体标头。
通用标头:同时适用于请求和响应消息,但与最终消息传输的数据无关的消息头。
请求标头:包含更多有关要获取的资源或客户端本身信息的消息头。
响应标头:包含有关响应的补充信息,如其位置或服务器本身(名称和版本等)的消息头。
实体标头:包含有关实体主体的更多信息,比如主体长(Content-Length)度或其MIME类型。
现在的一个分类情况是:HTTP 标头(header) - MDN Web 文档术语表:Web 相关术语的定义 | MDN (mozilla.org),但是还是要参考现在浏览器通用的HTTP版本下的分类,现在用的基本都是HTTP1.1版本。
而一个完整的请求头/响应头,应该除了自身,还包括通用标头和实体标头:
通用标头(General headers)
- Cache-Control——控制缓存的行为; 详情
- Connection(禁止修改)——决定当前的事务完成后,是否会关闭网络连接; 详情
- Date——创建报文的日期时间; 详情
- Keep-Alive——用来设置超时时长和最大请求数;详情
- Via(禁止修改)——这个消息首部可以用来追踪消息转发情况,防止循环请求,以及识别在请求或响应传递链中消息发送者对于协议的支持能力。;详情
- Warning——错误通知;详情
- Trailer——允许发送方在分块发送的消息后面添加额外的元信息; 详情
- Upgrade——升级为其他协议;
请求标头(Request headers)
- Accept——客户端可以处理的内容类型;并使用 Content-Type 应答头通知客户端它的选择详情
- Accept-Encoding(禁止修改)——客户端能够理解的内容编码方式(gzip|br|deflate);详情
- Accept-Language(禁止修改)——客户端可以理解的自然语言;详情
- Authorization——含有服务器用于验证用户代理身份的凭证,通常会在服务器返回401 Unauthorized 状态码以及WWW-Authenticate 消息头之后在后续请求中发送此消息头;详情
- Cookie——通过Set-Cookie设置的值;详情
- DNT——表明用户对于网站追踪的偏好;详情
- From——用户的电子邮箱地址;详情
- Host(禁止修改)——请求资源所在服务器;详情
- If-Match——比较实体标记(ETag);详情
- If-Modified-Since——比较资源的更新时间;详情
- If-None-Match——比较实体标记(与 If-Match 相反);详情
- If-Range——资源未更新时发送实体 Byte 的范围请求;详情
- If-Unmodified-Since——比较资源的更新时间(与 If-Modified-Since 相反);详情
- Origin(禁止修改)——表明了请求来自于哪个站点;详情
- Proxy-Authorization——代理服务器要求客户端的认证信息;详情
- Range——告知服务器返回文件的哪一部分。在一个 Range 首部中,可以一次性请求多个部分,服务器会以 multipart 文件的形式将其返回;详情
- Referer(禁止修改)——包含了当前请求页面的来源页面的地址,即表示当前页面是通过此来源页面里的链接进入的;详情
- Sec-fetch-*(禁止修改) ——获取元数据标头指示请求的目的地,即如何使用获取的数据。
- Upgrade-Insecure-Requests——表示客户端优先选择加密及带有身份验证的响应;详情 - User-Agent——浏览器信息;详情
响应标头(Response Headers)
- Access-Control-*——响应头用于在请求跨域时的处理。详情 - - - - Accept-Ranges——是否接受字节范围请求;详情
- Age——消息对象在缓存代理中存贮的时长,以秒为单位;详情
- Content-Disposition——回复的内容该以何种形式展示,是以内联的形式(预览),还是以附件的形式下载并保存到本地。(inline|attachment)
- Content-Security-Policy——允许站点管理者在指定的页面控制用户代理的资源;详情 - ETag——缓存未更改的资源;链接描述
- Expires——包含日期/时间, 即在此时候之后,响应过期;详情
- Last-Modified——其中包含源头服务器认定的资源做出修改的日期及时间。 它通常被用作一个验证器来判断接收到的或者存储的资源是否彼此一致;详情
- Location——令客户端重定向至指定 URI;详情
- Proxy-Authenticate——代理服务器对客户端的认证信息;详情
- Public-Key-Pins——包含该Web 服务器用来进行加密的 public key (公钥)信息;详情
- Public-Key-Pins-Report-Only——设置在公钥固定不匹配时,发送错误信息到report-uri;详情
- Referrer-Policy——用来监管哪些访问来源信息——会在 Referer 中发送;详情
- Retry-After——表示用户代理需要等待多长时间之后才能继续发送请求 Server——首部包含了处理请求的源头服务器所用到的软件相关信息。;详情
- Set-Cookie——服务器端向客户端发送 cookie;详情
- SourceMap——HTTP 响应头链接生成的代码到一个 source map,使浏览器能够重建原始的资源然后显示在调试器里。
- Strict-Transport-Security——它告诉浏览器只能通过HTTPS访问当前资源;详情
- Timing-Allow-Origin——用于指定特定站点,以允许其访问Resource Timing API提供的相关信息;详情
- Transfer-Encoding(禁止修改)——传递给用户所采用的编码形式(gzip|deflate);详情
- Vary——它决定了对于未来的一个请求头,应该用一个缓存的回复 (response) 还是向源服务器请求一个新的回复;详情
- WWW-Authenticate——定义了使用何种验证方式去获取对资源的连接;详情
- X-XSS-Protection——当检测到跨站脚本攻击 (XSS)时,浏览器将停止加载页面;详情
实体标头(Entity Headers)
- Allow——枚举资源所支持的 HTTP 方法的集合(GET, POST, HEAD);详情
- Content-Encoding——用于对特定媒体类型的数据进行压缩(gzip|br|deflate);详情
- Content-Language——访问者希望采用的语言或语言组合(需配合html lang=”de”);详情
- Content-Length(禁止修改)——发送给接收方的消息主体的大小;详情
- Content-Location——替代对应资源的 URI;详情
- Content-Range——实体主体的位置范围;详情
- Content-Security-Policy——允许站点管理者控制用户代理能够为指定的页面加载哪些资源,这将帮助防止跨站脚本攻击
- Content-Type——客户端告诉服务器实际发送的数据类型,服务端告诉客户端返回的内容类型(text/html; charset=utf-8);详情
Content-Type标头
Content-Type:
响应标头是告知客户端内容的类型,客户端再根据这个信息将内容正确地呈现给用户。
常见的内容类型有:
text/html
—— HTML 文档text/plain
—— 文本内容text/css
—— CSS 样式文件text/javascript
—— JS 脚本文件application/json
—— JSON 格式的数据application/xml
—— XML 格式的数据image/png
—— PNG 图片
添加设置标头
w是http.ResponseWriter对象
w.Header().Set("Content-Type", "text/html; charset=utf-8")
我们怎么知道w对象所代表的http.ResponseWriter
有哪些接口呢?看Go官方文档,见下面。
进入文档定位到net/http:
在索引里定位 type ResponseWriter
的定义(ctrl F):
滚动下来,点击 Example
取消折叠,即可看到示例代码:
添加404响应状态码
Web数据响应
响应分为三个部分:
响应行:协议、响应状态和状态描述,如:HTTP/1.1 200 OK
响应标头:包含各自头部字段信息,如cookie,Content-Type等头部信息
响应体:携带客户端想要的数据,格式与编码由头部的Content-Type决定
响应状态码的固定取值和意义
100~199:表示服务端成功接收客户端请求,要求客户端继续提交下一次请求才能完成整个处理过程。
200~299:表示服务端成功接收请求并已完成整个处理流程。最常用就是:200。
300~399:为完成请求,客户端需要进一步细化请求。比较常用的如:客户端请求的资源已经移动一个新地址使用302表示将资源重定向,客户端请求的资源未发生改变,使用304,告诉客户端从本地缓存中获取。
400~499:客户端的请求有错误,如;404表示你请求的资源在web服务器中找不到,403表示服务器拒绝客户端的访问,一般是权限不够。
500~599:服务器端出现错误,最常用的是:500
设置响应状态码
首先通过查看文档可知道有这个函数可以设置:
w.WriteHeader(http.StatusOK)
然后定位到设置状态码的常量:
最后选中我们的404状态码,在代码中添加:
w.WriteHeader(http.StatusNotFound)
学会查阅Go在线文档
Go标准库:golang.google.cn/pkg
那么如何在不联网的情况查看go文档呢?
因为我们要经常查阅Go语言官方文档,可惜因国内访问国外网不稳定,golang.org经常出现无法访问的情况。幸运的是Go团队提供了godoc
工具,允许我们在本地直接访问Go文档。
先安装godoc
工具:
$ go install golang.org/x/tools/cmd/godoc@latest
然后在本地运行起来:
$ godoc -http=:6060
最后浏览器里访问:
localhost:6060/doc:是一些 Go 的主要文档,如发布日志、Effective Go 等;
localhost:6060/pkg:是标准库以及加载过的第三方库的文档。
本作品采用《CC 协议》,转载必须注明作者和本文链接