//helloWorldWeb.go
//go run helloWorldWeb.go
//127.0.0.1
package main
import (
"fmt"
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World")
}
func main() {
server := &http.Server {
Addr: "0.0.0.0:80",
}
http.HandleFunc("/", hello)
server.ListenAndServe()
}
HTTP(Hyper Text Transfer Protocal,超文本传输协议),简单请求-响应协议,运行在TCP协议上,无状态。它指定客户端发送给服务器端的消息和得到的响应。请求和响应消息头是ASCII码;消息内容则类似MIME格式。
客户端发送到服务器端的请求消息。
请求方法、URI、HTTP协议/协议版本组成。
请求方法 | 方法描述 |
---|---|
GET | 请求页面,并返回页面内容,请求参数包含在URL中,提交数据最多1024byte |
HEAD | 类似GET,只获取报头 |
POST | 提交表单或上传文件,数据(含请求参数)包含在请求体中 |
PUT | 取代指定内容的文档 |
DELETE | 删除指定资源 |
OPTIONS | 查看服务器的性能 |
CONNECT | 服务器当作跳板,访问其他网页 |
TRACE | 回显服务器收到的请求,用于测试或诊断 |
请求头 | 示例 | 说明 |
---|---|---|
Accept | Accept: text/plain, text/html | 客户端能够接收的内容类型 |
Accept-charset | Accept-charset: iso-8859-5 | 字符编码集 |
Accept-Encoding | Accept-Encoding: compress, gzip | 压缩编码类型 |
Accept-Language | Accept-Language: en, zh | 语言 |
Accept-Ranges | Accept-Ranges: bytes | 子范围字段 |
Authorization | Authorization: Basic dbXleoOEpePOetpoe2Ftyd== | 授权证书 |
Cache-Control | Cache-Control: no-cache | 缓存机制 |
Connection | Connection: close | 是否需要持久连接(HTTP1.1默认持久连接) |
Cookie | Cookie: $version=1; Skin=new; | 请求域名下的所有cookie值 |
Content-Length | Content-Length: 348 | 内容长度 |
HTTP请求中传输数据的实体。
服务器端返回给客户端。
表示服务器的响应状态。
状态码 | 说明 | 详情 |
---|---|---|
100 | 继续 | 服务器收到部分请求,等待客户端继续提出请求 |
101 | 切换协议 | 请求者已要求服务器切换协议,服务器已确认并准备切换协议 |
200 | 成功 | 成功处理请求 |
201 | 已创建 | 服务器创建了新的资源 |
202 | 已接受 | 已接收请求,但尚未处理 |
203 | 非授权信息 | 成功处理请求,但返回信息来自另一个源 |
204 | 无内容 | 成功处理请求,无返回内容 |
205 | 重置内容 | 成功处理请求,内容重置 |
206 | 部分内容 | 成功处理部分内容 |
300 | 多种选择 | 可执行多种操作 |
301 | 永久移动 | 永久重定向 |
302 | 临时移动 | 暂时重定向 |
303 | 查看其他位置 | 重定向目标文档应通过GET获取 |
304 | 未修改 | 使用上次网页资源 |
305 | 使用代理 | 应使用代理访问 |
307 | 临时重定向 | 临时从其他位置响应 |
400 | 错误请求 | 无法解析 |
401 | 未授权 | 无身份验证或验证未通过 |
403 | 禁止访问 | 拒绝 |
404 | 未找到 | 找不到 |
405 | 方法禁用 | 禁用指定方法 |
406 | 不接受 | 无法使用内容响应 |
407 | 需要代理授权 | 需要使用代理授权 |
408 | 请求超时 | 请求超时 |
409 | 冲突 | 完成请求时发生冲突 |
410 | 已删除 | 资源永久删除 |
411 | 需要有效长度 | 不接受标头字段不含有效内容长度 |
412 | 未满足前提条件 | 服务器未满足某个前提条件 |
413 | 请求实体过大 | 超出能力 |
414 | 请求URI过长 | 网址过长,无法处理 |
415 | 不支持类型 | 格式不支持 |
416 | 请求范围不符 | 页面无法提供请求范围 |
417 | 未满足期望值 | 未满足期望请求标头字段 |
500 | 服务器内部发生错误 | 服务器错误 |
501 | 未实现 | 不具备功能 |
502 | 错误网关 | 收到无效响应 |
503 | 服务不可用 | 无法使用 |
504 | 网关超时 | 没及时收到请求 |
505 | HTTP版本不支持 | 不支持HTTP协议版本 |
包含服务器对请求的应答信息。
响应头 | 说明 |
---|---|
Allow | 服务器支持的请求方法 |
Content-Encondig | 文档编码方法。 |
Content-Length | 内容长度,浏览器持久HTTP连接时需要 |
Content-Type | 文档的MIME类型 |
Date | GMT时间 |
Expires | 过期时间后,不再缓存 |
Last-Modified | 文档最后改动时间。通过比较客户端头if-Modified-Since,可能返回304(Not Modified)。 |
Location | 客户端应去哪里提取文档。 |
Refresh | 浏览器应刷新时间,秒 |
Server | 服务器名字 |
Set-Cookie | 设置页面关联Cookie |
WWW-Authenticate | 客户应在Authorization中提供授权信息,通常返回401。 |
HTTP请求返回的内容。
HTML,二进制数据,JSON文档,XML文档等。
URI(Uniform Resource Identifier,统一资源标识符)
用来标识Web上每一种可用资源,概念。由资源的命名机制、存放资源的主机名、资源自身的名称等组成。
URL(Uniform Resource Locator,统一资源定位符)
用于描述网络上的资源(描述信息资源的字符串),实现。使用统一格式,包括文件、服务器地址和目录等。
scheme://host[:port#]/path/.../[?query-string][#anchor]
//协议(服务方式)
//主机域名或IP地址(可含端口号)
//具体地址,目录和文件名等
HTTPS(Hyper Text Transfer Protocol over SecureSocket Layer),在HTTP基础上,通过传输加密和身份认证保证传输过程的安全型。HTTP + SSL/TLS。
TLS(Transport Layer Security,传输层安全性协议),及其前身SSL(Secure Socket Layer,安全套接字层),保障通信安全和数据完整性。
MVC软件架构模型
package main
import (
"net/http"
)
func sayHello(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("Hello World"))
}
func main() {
//注册路由
http.HandleFunc("/hello", sayHello)
//开启对客户端的监听
http.ListenAndServe(":8080", nil)
}
http.HandleFunc()函数
//输入参数:监听端口号和事件处理器handler
http.ListenAndServe()函数
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
package main
import (
"net/http"
)
type Refer struct {
handler http.Handler
refer string
}
func (this *Refer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Referer() == this.refer {
this.handler.ServeHTTP(w, r)
} else {
w.WriteHeader(403)
}
}
func myHandler(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("this is handler"))
}
func hello(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("hello"))
}
func main() {
referer := &Refer{
handler: http.HandlerFunc(myHandler),
refer: "www.shirdon.com",
}
http.HandleFunc("/hello", hello)
http.ListenAndServe(":8080", referer)
}
//curl --header "Referer:: www.shirdon.com" -v http://127.0.0.1:8080/hello
//证书文件路径,私钥文件路径
func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error
package main
import (
"log"
"net/http"
)
func handle(w http.ResponseWriter, r *http.Request) {
log.Printf("Got connection: %s", r.Proto)
w.Write([]byte("Hello this is a HTTP 2 message!"))
}
func main() {
srv := &http.Server{Addr: ":8088", Handler: http.HandlerFunc(handle)}
log.Printf("Serving on https://0.0.0.0:8088")
log.Fatal(srv.ListenAndServeTLS("server.crt", "server.key"))
}
//src/net/http/client.go
var DefaultClient = &Client{}
func Get(url string) (resp *Response, err error) {
return DefaultClient.Get(url)
}
func (c *Client) Get(url string) (resp *Response, err error) {
req, err := NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
return c.Do(req)
}
func Post(url, contentType string, body io.Reader) (resp *Response, err error) {
return DefaultClient.Post(url, contentType, body)
}
func (c *Client) Post(url, contentType string, body io.Reader) (resp *Response, err error) {
req, err := NewRequest("POST", url, body)
if err != nil {
return nil, err
}
req.Header.set("Content-Type", contentType)
return c.Do(req)
}
func NewRequest(method, url string, body io.Reader) (*Request, error)
//请求类型
//请求地址
//若body实现io.Closer接口,则Request返回值的Body字段会被设置为body值,并被Client的Do()、Post()和PostForm()方法关闭。
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
resp, err := http.Get("https://www.baidu.com")
if err != nil {
fmt.Println("err:", err)
}
closer := resp.Body
bytes, err := ioutil.ReadAll(closer)
fmt.Println(string(bytes))
}
package main
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
url := "https://www.shirdon.com/comment/add"
body := `{"userId": 1, "articleId": 1, "comment": 这是一条评论}`
resp, err := http.Post(url, "application/x-www-form-urlencoded", bytes.NewBuffer([]byte(body)))
if err != nil {
fmt.Println("err:", err)
}
bytes, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(bytes))
}
package main
import (
"fmt"
"io/ioutil"
"net/http"
"strings"
)
func main() {
url := "https://www.shirdon.com/comment/update"
payload := strings.NewReader(`{"userId": 1, "articleId": 1, "comment": 这是一条评论}`)
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println("err:", err)
}
defer res.Body.Close()
bytes, err := ioutil.ReadAll(res.Body)
fmt.Println(string(res))
fmt.Println(string(bytes))
}
package main
import (
"fmt"
"io/ioutil"
"net/http"
"strings"
)
func main() {
url := "https://www.shirdon.com/comment/delete"
payload := strings.NewReader(`{"userId": 1, "articleId": 1, "comment": 这是一条评论}`)
req, _ := http.NewRequest("DELETE", url, payload)
req.Header.Add("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println("err:", err)
}
defer res.Body.Close()
bytes, err := ioutil.ReadAll(res.Body)
fmt.Println(string(res))
fmt.Println(string(bytes))
}
type Header map[string][]string
headers := http.Header{"token": {"feeowiwpor23dlspweh"}}
headers.Add("Accept-Charset", "UTF-8")
headers.Set("Host", "www.shirdon.com")
headers.Set("Location", "www.baidu.com")
text/template处理任意格式的文本,html/template生成可对抗代码注入的安全html文档。
模板和模板引擎
模板是事先定义好的不变的html文档,模板渲染使用可变数据替换html文档中的标记。模板用于显示和数据分离(前后端分离)。模板技术,本质是模板引擎利用模板文件和数据生成html文档。
Go语言模板引擎
使用:
(1)定义模板文件
按照相应语法规则去定义。
(2)解析模板文件
创建指定模板名称的模板对象
func New(name string) *Template
解析模板内容
func (t *Template) Parse(src string) (*Template, error)
解析模板文件
func ParseFiles(filenames...string) (*Template, error)
正则匹配解析文件,template.ParaeGlob(“a*”)
func ParseGlob(pattern string) (*Template, error)
(3)渲染模板文件
func (t *Template) Execute(wr io.Writer, data interface{}) error
//配合ParseFiles()函数使用,需指定模板名称
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>模板使用示例</title>
</head>
<body>
<p>加油,小伙伴, {{ . }} </p>
</body>
</html>
package main
import (
"fmt"
"html/template"
"net/http"
)
func helloHandleFunc(w http.ResponseWriter, r *http.Request) {
// 1. 解析模板
t, err := template.ParseFiles("./template_example.tmpl")
if err != nil {
fmt.Println("template parsefile failed, err:", err)
return
}
// 2.渲染模板
name := "我爱Go语言"
t.Execute(w, name)
}
func main() {
http.HandleFunc("/", helloHandleFunc)
http.ListenAndServe(":8086", nil)
}
type UserInfo struct {
Name string
Gender string
Age int
}
func sayHello(w http.ResponseWriter, r *http.Request) {
tmpl, err := template.ParseFiles("./hello.html")
if err != nil {
fmp.Println("create template failed, err:", err)
return
}
user := UserInfo {
Name: "张三",
Gender: "男",
Age: 28,
}
tmpl.Execute(w, user)
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Hello</title>
</head>
<body>
<p>Hello {{.Name}}</p>
<p>性别:{{.Gender}}</p>
<p>年龄:{{.Age}}</p>
</body>
</html>
常用语法:
{{/* 这是一个注释,不会解析 */}}
$variable := pipeline
{{if pipeline}} T1 {{end}}
{{if pipeline}} T1 {{else}} T0 {{end}}
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
{{range pipeline}} T1 {{end}}
{{range pipeline}} T1 {{else}} T0 {{end}}
package main
import (
"log"
"os"
"text/template"
)
func main() {
//创建一个模版
rangeTemplate := `
{{if .Kind}}
{{range $i, $v := .MapContent}}
{{$i}} => {{$v}} , {{$.OutsideContent}}
{{end}}
{{else}}
{{range .MapContent}}
{{.}} , {{$.OutsideContent}}
{{end}}
{{end}}`
str1 := []string{"第一次 range", "用 index 和 value"}
str2 := []string{"第二次 range", "没有用 index 和 value"}
type Content struct {
MapContent []string
OutsideContent string
Kind bool
}
var contents = []Content{
{str1, "第一次外面的内容", true},
{str2, "第二次外面的内容", false},
}
// 创建模板并将字符解析进去
t := template.Must(template.New("range").Parse(rangeTemplate))
// 接收并执行模板
for _, c := range contents {
err := t.Execute(os.Stdout, c)
if err != nil {
log.Println("executing template:", err)
}
}
}
/*
//输出
0 => 第一次 range, 第一次外面的内容
1 => 用 index 和 value, 第一次外面的内容
第二次 range, 第二次外面的内容
没有用 index 和 value, 第二次外面的内容
*/
{{with pipeline}} T1 {{end}}
{{with pipeline}} T1 {{else}} T0 {{end}}
{{eq arg1 arg2 arg3}}
eq
ne
lt
le
gt
ge
函数名 | 功能 |
---|---|
and | 返回第1个空参数或最后一个参数,所有参数都执行。and x y等价于if x then y else x |
or | 返回第1个非空参数或最后一个参数,所有参数都执行。and x y等价于if x then y else x |
not | 非 |
len | 长度 |
index | index y 1 2 3, index[1][2][3] |
fmt.Sprint | |
printf | fmt.Sprintf |
println | fmt.Sprintln |
html | html逸码等价表示 |
urlquery | 可嵌入URL查询的逸码等价表示 |
js | JavaScript逸码等价表示 |
call | call func a b, func(a, b);1或2个返回值,第2个为error,非nil会中断并返回给调用者。 |
func (t *Template) Funcs(funcMap FuncMap) *Template
FuncMap映射函数要求1或2个返回值,第2个为error,非nil会中断并返回给调用者。
type FuncMap map[string]interface{}
package main
import (
"fmt"
"html/template"
"io/ioutil"
"net/http"
)
func Welcome() string { //没参数
return "Welcome"
}
func Doing(name string) string { //有参数
return name + ", Learning Go Web template "
}
func sayHello(w http.ResponseWriter, r *http.Request) {
htmlByte, err := ioutil.ReadFile("./funcs.html")
if err != nil {
fmt.Println("read html failed, err:", err)
return
}
// 自定义一个匿名模板函数
loveGo := func() (string) {
return "欢迎一起学习《Go Web编程实战派从入门到精通》"
}
// 采用链式操作在Parse()方法之前调用Funcs添加自定义的loveGo函数
tmpl1, err := template.New("funcs").Funcs(template.FuncMap{"loveGo": loveGo}).Parse(string(htmlByte))
if err != nil {
fmt.Println("create template failed, err:", err)
return
}
funcMap := template.FuncMap{
//在FuncMap中声明相应要使用的函数,然后就能够在template字符串中使用该函数
"Welcome": Welcome,
"Doing": Doing,
}
name := "Shirdon"
tmpl2, err := template.New("test").Funcs(funcMap).Parse("{{Welcome}}<br/>{{Doing .}}")
if err != nil {
panic(err)
}
// 使用user渲染模板,并将结果写入w
tmpl1.Execute(w, name)
tmpl2.Execute(w, name)
}
func main() {
http.HandleFunc("/", sayHello)
http.ListenAndServe(":8087", nil)
}
funcs.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>tmpl test</title>
</head>
<body>
<h1>{{loveGo}}</h1>
</body>
</html>
{{define "name"}} T {{end}}
{{template "name"}}
{{template "name" pipeline}}
{{block "name" pipeline}} T {{end}}
//等价于
{{define "name"}} T {{end}}
{{template "name" pipeline}}
t.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>tmpl test</title>
</head>
<body>
<h1>测试嵌套template语法</h1>
<hr>
{{template "ul.html"}}
<hr>
{{template "ol.html"}}
</body>
</html>
{{define "ol.html"}}
<h1>这是ol.html</h1>
<ol>
<li>I love Go</li>
<li>I love java</li>
<li>I love c</li>
</ol>
{{end}}
ul.html
<ul>
<li>注释</li>
<li>日志</li>
<li>测试</li>
</ul>
package main
import (
"fmt"
"html/template"
"net/http"
)
//定义一个UserInfo结构体
type UserInfo struct {
Name string
Gender string
Age int
}
func tmplSample(w http.ResponseWriter, r *http.Request) {
tmpl, err := template.ParseFiles("./t.html", "./ul.html")
if err != nil {
fmt.Println("create template failed, err:", err)
return
}
user := UserInfo{
Name: "张三",
Gender: "男",
Age: 28,
}
tmpl.Execute(w, user)
fmt.Println(tmpl)
}
func main() {
http.HandleFunc("/", tmplSample)
http.ListenAndServe(":8087", nil)
}