做过后端开发的应该都知道,我们遇到最多的就是返回数据给客户端,就和客户端要搭界面一样,重复操作着,但是却有必不可少,今天我们来简单说说Golang Response Snippets
Golang Response Snippets: JSON, XML and more
Taking inspiration from the Rails layouts and rendering guide, I thought it’d be a nice idea to build a snippet collection illustrating some common HTTP responses for Go web applications.
只返回header
对于一些请求而言,不需要返回任何的数据,只是返回一个header即可,大大提高了返回服务器响应速度。
先了解一下net/http包中的几个方法:
//给一个key设定为响应的value.
func (h Header) Set(key, value string)
// WriteHeader该方法发送HTTP回复的头域和状态码。如果没有被显式调用,第一次调用Write时会触发隐式调用WriteHeader(http.StatusOK)。因此,显示调用WriterHeader主要用于发送错误状态码。
WriteHeader(int)
在使用的时候,我们可以这么做
package main
import (
"net/http"
)
func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":8080", nil)
}
func foo(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Server", "A Go Web Server")
w.WriteHeader(200)
}
通过curl进行请求:curl -i localhost:8080
curl -i localhost:8080
HTTP/1.1 200 OK
Server: A Go Web Server
Date: Mon, 29 Jan 2018 02:52:41 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8
返回文本
这个不常用,但是也介绍一下而已。
用到的方法:
// Write向连接中写入数据,该数据作为HTTP response的一部分。如果被调用时还没有调用WriteHeader,本方法会先调用
WriteHeader(http.StatusOK)。
//如果Header中没有"Content-Type"键,本方法会使用包函数DetectContentType检查数据的前512字节,将返回值作为该键的值
Write([]byte) (int, error)
返回文本实际使用方法
package main
import (
"net/http"
)
func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":8080", nil)
}
func foo(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("I am Gopher"))
}
通过curl进行请求:curl -i localhost:8080
curl -i localhost:8080
HTTP/1.1 200 OK
Date: Mon, 29 Jan 2018 03:02:00 GMT
Content-Length: 11
Content-Type: text/plain; charset=utf-8
I am Gopher
返回JSON
Go语言里的标准库”encoding/json”
转换对应表:
bool类型 转换成JSON中的boolean
整型,浮点型转换成JSON中的Number
字符串转换成JSON中的字符串(""而不是'')
结构体转换成JSON中的Object
[]byte 会先base64然后转换成JSON中的字符串(""而不是'')
map 转换成JSON中的Object
interface{} 会按内部的类型进行实际转换
nil 会转换成JSON中的Null
encodeing/json几乎常用的就两个方法:
func Marshal(v interface{}) ([]byte, error)
func Unmarshal(data []byte, v interface{}) error
顾名思义“Marshal”将Go对象进行转换成JSON,而”Unmarshal”则是将JSON转换成Go对象。
package main
import (
"encoding/json"
"net/http"
)
// 为了对应关系可以一一对应上,我们需要手动的为结构体打上Tag,才能转换到正确的JSON
// structTag还有一些其他有用的属性,比如:
// omitempty 如果JSON字段为空则忽略
// - 直接忽略
type Profile struct {
Name string `json:"name"`
Hobbies []string `json:"hobbies"`
}
func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":8080", nil)
}
func foo(w http.ResponseWriter, r *http.Request) {
profile := Profile{"SuperWang", []string{"football", "programming"}}
js, err := json.Marshal(profile)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(js)
}
有时候你想更灵活的使用JSON,这个时候就要用到json.RawMessage,多数情况下,我们不会使用到它。在JSON中,有一个字段的格式是未知的,比如可能是string,也可能是int,那么这个时候就要用到*json.RawMessage了。
不过在转换的过程中,如果我们定义的是结构体,跟Map会有一些不同,以下几点是要注意的:
结构体的成员必须是大写开头
使用Marshal时会按结构体成员的变量名做为Key
Unmarshal时会自动匹配结构体成员,大小写不敏感,如果JSON中有多余字段,会直接抛弃,如果缺少某个字段,则会忽略对结构体成员赋值
通过curl进行请求:curl -i localhost:8080
curl -i localhost:8080
HTTP/1.1 200 OK
Content-Type: application/json
Date: Mon, 29 Jan 2018 03:10:52 GMT
Content-Length: 57
{"name":"SuperWang","hobbies":["football","programming"]}
返回XML
很久之前,很多人讨论xml和json孰是孰非,渐渐地xml越来越被人们遗忘。
但是一些接口还是需要使用xml的,比如xmpp协议。
package main
import (
"encoding/xml"
"net/http"
)
type Profile struct {
Name string
Hobbies []string `xml:"Hobbies>Hobby"`
}
func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":8080", nil)
}
func foo(w http.ResponseWriter, r *http.Request) {
profile := Profile{"SuperWang", []string{"football", "programming"}}
x, err := xml.MarshalIndent(profile, "", " ")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/xml")
w.Write(x)
}
通过curl进行请求:curl -i localhost:8080
curl -i localhost:8080
HTTP/1.1 200 OK
Content-Type: application/xml
Date: Mon, 29 Jan 2018 03:16:00 GMT
Content-Length: 129
<Profile>
<Name>SuperWang</Name>
<Hobbies>
<Hobby>football</Hobby>
<Hobby>programming</Hobby>
</Hobbies>
</Profile>
返回文件
通过接口,返回一张图片,一个文本文件等等,都是很常见的。
package main
import (
"net/http"
"path"
)
func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":8080", nil)
}
func foo(w http.ResponseWriter, r *http.Request) {
fp := path.Join("images", "foo.png")
http.ServeFile(w, r, fp)
}
建一个images文件夹,放入foo.png文件,运行,
运行,浏览器输入:
返回HTML
下面是返回一个HTML的网页。
package main
import (
"html/template"
"net/http"
"path"
)
type Profile struct {
Name string
Hobbies []string
}
func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":8080", nil)
}
func foo(w http.ResponseWriter, r *http.Request) {
profile := Profile{"SpuerWang", []string{"snowboarding", "programming"}}
fp := path.Join("templates", "index.html")
tmpl, err := template.ParseFiles(fp)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tmpl.Execute(w, profile); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
新建文件夹templates,在里面新建文件inde.html:
<h1>Title {{ .Name }}</h1>
<p>.....</p>