Go Web服务就是向其它软件程序提供服务的程序。
Web服务的终端用户(end user)不是人类,而是软件程序,通过HTTP进行通信。
SOAP优点:
安全、健壮;
能使用WSDL(web service description language)进行明确描述。
SOAP缺点:
笨重、复杂;
XML报文冗长,难以调试;
额外资源损耗而无法高效运行。
REST优点:
非结构而是设计理念,灵活简单;
基于JSON简单数据格式而非XML,运行高效。
SOAP,功能驱动,用于实现内部应用的企业集成(enterprise integration);
REST,数据驱动,关注资源,HTTP方法是操作资源的动词,用于服务外部和第三方的开发者。
Simple Object Access Protocol(简单对象访问协议,名不符实)。
SOAP高度结构化,严格定义,用于传输数据的XML非常复杂。
大多数基于SOAP的Web服务通过HTTP的POST方法(Content-Type:application/soap+xml)发送SOAP报文。
WSDL报文冗长。
REST(Representational State Transfer,具象状态传输),设计理念,设计通过标准几个动作(verb,POST、GET、PUT、DELETE、PATCH等)操作资源(URL),以此相互交流的程序。
类似数据库的CRUD。
| HTTP方法 | 使用 | 示例 |
|---|---|---|
| POST | 创建新资源(新URL) | POST /users |
| GET | 获取资源 | GET /users/1 |
| PUT | 替换已存在资源 | PUT /users/1 |
| DELETE | 删除资源 | DELETE /users/1 |
| PATCH | 资源部分更新 | PATCH /users/1 |
REST只支持指定的几个HTTP方法操作资源,ACTIVATE /user/456 HTTP/1.1 不支持。
对过程或动作进行建模的常用方法:
(1)过程具象化(抽象概念转为实际数据模型或对象),或者动作转换为名词,将其用作资源;
(2)动作用作资源的属性。
POST /user/456/activation HTTP/1.1
{"date": "2015-05-15T13:05:05Z"}
给用户456的activation资源附加了日期属性。
PATCH /user/456 HTTP/1.1
{"active": "true"}
将用户456资源的active属性设置为true。
(1)创建存储XML数据的结构;
(2)xml.Unmarshal解封XML数据到结构里。
post.xml
<?xml version="1.0" encoding="utf-8"?>
<post id="1">
<content>Hello World!</content>
<author id="2">Sau Sheong</author>
</post>
package main
import (
"encoding/xml"
"fmt"
"io/ioutil"
"os"
)
//``包含的键值对叫结构标签(struct tag),用来映射结构和XML元素
//处理XML时,键为xml,值为""包含字符串
//结构名称及包含的所有字段必须都以大写字母开头(公开)
//结构标签使用规则:
//(1)XMLName xml.Name `xml:"post"`存储XML元素名字(post一般与结构名字Post相同,可以不同)
//(2)`xml:",attr"`存储XML元素中attr_name属性的值
//比如Id string `xml:"id,attr"`
//(3)Tag_name string `xml:",chardata"`存储(Tag_name任意名字)XML元素的内部字符数据,比如
/*
type Author struct {
Id string `xml:"id,attr"` //author的id属性值
Name string `xml:",chardata"` //author的内部字符数据
}
*/
//(4)Tag_name string `xml:",innerxml"`存储XML元素内部原始XML(Tag_name任意名字)
//比如Xml string `xml:",innerxml"`
//(5)无模式标志(attr、chardata、innerxml)的结构字段与同名XML元素匹配
//比如Content string `xml:"content"`
//(6)`xml:"a>b>c"`直接获取指定XML元素,a和b为中间元素,c为获取的节点元素
type Post struct {
XMLName xml.Name `xml:"post"` //post
Id string `xml:"id,attr"` //1
Content string `xml:"content"` //Hello World!
Author Author `xml:"author"`
Xml string `xml:",innerxml"` //Hello World! Sau Sheong
}
type Author struct {
Id string `xml:"id,attr"`
Name string `xml:",chardata"`
}
func main() {
xmlFile, err := os.Open("post.xml")
if err != nil {
fmt.Println("Error opening XML file:", err)
return
}
defer xmlFile.Close()
xmlData, err := ioutil.ReadAll(xmlFile)
if err != nil {
fmt.Println("Error reading XML data:", err)
return
}
var post Post
xml.Unmarshal(xmlData, &post)
fmt.Println(post)
}
post.xml
<?xml version="1.0" encoding="utf-8"?>
<post id="1">
<content>Hello World!</content>
<author id="2">Sau Sheong</author>
<comments>
<comment id="1">
<content>Have a great day!</content>
<author>Adam</author>
</comment>
<comment id="2">
<content>How are you today?</content>
<author>Betty</author>
</comment>
</comments>
</post>
package main
import (
"encoding/xml"
"fmt"
"io/ioutil"
"os"
)
type Post struct {
XMLName xml.Name `xml:"post"`
Id string `xml:"id,attr"`
Content string `xml:"content"`
Author Author `xml:"author"`
Xml string `xml:",innerxml"`
Comments []Comment `xml:"comments>comment"`
}
type Author struct {
Id string `xml:"id,attr"`
Name string `xml:",chardata"`
}
type Comment struct {
Id string `xml:"id,attr"`
Content string `xml:"content"`
Author Author `xml:"author"`
}
func main() {
xmlFile, err := os.Open("post.xml")
if err != nil {
fmt.Println("Error opening XML file:", err)
return
}
defer xmlFile.Close()
xmlData, err := ioutil.ReadAll(xmlFile)
if err != nil {
fmt.Println("Error reading XML data:", err)
return
}
var post Post
xml.Unmarshal(xmlData, &post)
fmt.Println(post.XMLName.Local)
fmt.Println(post.Id)
fmt.Println(post.Content)
fmt.Println(post.Author)
fmt.Println(post.Xml)
fmt.Println(post.Author.Id)
fmt.Println(post.Author.Name)
fmt.Println(post.Comments)
fmt.Println(post.Comments[0].Id)
fmt.Println(post.Comments[0].Content)
fmt.Println(post.Comments[0].Author)
fmt.Println(post.Comments[1].Id)
fmt.Println(post.Comments[1].Content)
fmt.Println(post.Comments[1].Author)
}
高效地处理以流(stream)方式传输的XML文件以及体积较大的XML文件,使用Decoder结构代替Unmarshal函数,通过手动解码XML元素的方式来解封XML数据。
创建存储XML数据的结构
↓
创建解码XML的解码器
↓
遍历XML文件并将数据解码至结构
<?xml version="1.0" encoding="utf-8"?>
<post id="1">
<content>Hello World!</content>
<author id="2">Sau Sheong</author>
<comments>
<comment id="1">
<content>Have a great day!</content>
<author id="3">Adam</author>
</comment>
<comment id="2">
<content>How are you today?</content>
<author id="4">Betty</author>
</comment>
</comments>
</post>
package main
import (
"encoding/xml"
"fmt"
"io"
"os"
)
type Post struct {
XMLName xml.Name `xml:"post"`
Id string `xml:"id,attr"`
Content string `xml:"content"`
Author Author `xml:"author"`
Xml string `xml:",innerxml"`
Comments []Comment `xml:"comments>comment"`
}
type Author struct {
Id string `xml:"id,attr"`
Name string `xml:",chardata"`
}
type Comment struct {
Id string `xml:"id,attr"`
Content string `xml:"content"`
Author Author `xml:"author"`
}
func main() {
xmlFile, err := os.Open("post.xml")
if err != nil {
fmt.Println("Error opening XML file:", err)
return
}
defer xmlFile.Close()
decoder := xml.NewDecoder(xmlFile)
for {
t, err := decoder.Token()
if err == io.EOF {
break
}
if err != nil {
fmt.Println("Error decoding XML into tokens:", err)
return
}
switch se := t.(type) {
case xml.StartElement:
if se.Name.Local == "comment" {
var comment Comment
decoder.DecodeElement(&comment, &se)
fmt.Println(comment)
}
}
}
}
将结构封装(marshal)至XML
package main
import (
"encoding/xml"
"fmt"
"io/ioutil"
)
type Post struct {
XMLName xml.Name `xml:"post"`
Id string `xml:"id,attr"`
Content string `xml:"content"`
Author Author `xml:"author"`
}
type Author struct {
Id string `xml:"id,attr"`
Name string `xml:",chardata"`
}
func main() {
post := Post{
Id: "1",
Content: "Hello World!",
Author: Author{
Id: "2",
Name: "Sau Sheong",
},
}
//output, err := xml.Marshal(&post)
//指定前缀和缩进
output, err := xml.MarshalIndent(&post, "", "\t\t")
if err != nil {
fmt.Println("Error marshalling to XML:", err)
return
}
//增加XML声明
err = ioutil.WriteFile("post.xml", []byte(xml.Header+string(output)), 0644)
if err != nil {
fmt.Println("Error writing XML to file:", err)
return
}
}
创建结构并填充数据
↓
创建XML文件
↓
创建用于编码结构的编码器
↓
通过编码器将结构编码至XML文件
将结构编码(encode)至XML
package main
import (
"encoding/xml"
"fmt"
"os"
)
type Post struct {
XMLName xml.Name `xml:"post"`
Id string `xml:"id,attr"`
Content string `xml:"content"`
Author Author `xml:"author"`
}
type Author struct {
Id string `xml:"id,attr"`
Name string `xml:",chardata"`
}
func main() {
post := Post{
Id: "1",
Content: "Hello World!",
Author: Author{
Id: "2",
Name: "Sau Sheong",
},
}
xmlFile, err := os.Create("post.xml")
if err != nil {
fmt.Println("Error creating XML file:", err)
return
}
defer xmlFile.Close()
//添加XML声明
xmlFile.Write([]byte(xml.Header))
encoder := xml.NewEncoder(xmlFile)
//指定前缀和缩进
encoder.Indent("", "\t")
err = encoder.Encode(&post)
if err != nil {
fmt.Println("Error encoding XML to file:", err)
return
}
}
JSON(JavaScritp Object Notation),衍生自JavaScript语言的一种轻量级的文本数据格式,设计理念是能被人类读懂,能被机器简单读取。
post.json
{
"id" : 1,
"content" : "Hello World!",
"author" : {
"id" : 2,
"name" : "Sau Sheong"
},
"comments" : [
{
"id" : 1,
"content" : "Have a great day!",
"author" : "Adam"
},
{
"id" : 2,
"content" : "How are you today?",
"author" : "Betty"
}
]
}
(1)创建用于包含JSON数据的结构;
(2)json.Unmarshal函数将JSON数据解封到结构里面。
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
type Post struct {
Id int `json:"id"`
Content string `json:"content"`
Author Author `json:"author"`
Comments []Comment `json:"comments"`
}
type Author struct {
Id int `json:"id"`
Name string `json:"name"`
}
type Comment struct {
Id int `json:"id"`
Content string `json:"content"`
Author string `json:"author"`
}
func main() {
jsonFile, err := os.Open("post.json")
if err != nil {
fmt.Println("Error opening JSON file:", err)
return
}
defer jsonFile.Close()
jsonData, err := ioutil.ReadAll(jsonFile)
if err != nil {
fmt.Println("Error reading JSON data:", err)
return
}
fmt.Println(string(jsonData))
var post Post
json.Unmarshal(jsonData, &post)
fmt.Println(post.Id)
fmt.Println(post.Content)
fmt.Println(post.Author.Id)
fmt.Println(post.Author.Name)
fmt.Println(post.Comments[0].Id)
fmt.Println(post.Comments[0].Content)
fmt.Println(post.Comments[0].Author)
}
创建存储JSON的结构
↓
创建解码JSON的解码器
↓
遍历JSON文件并用解码器将数据解码至结构
使用Decoder手动地将JSON数据解码到结构里面,处理流式JSON数据。
package main
import (
"encoding/json"
"fmt"
"io"
"os"
)
type Post struct {
Id int `json:"id"`
Content string `json:"content"`
Author Author `json:"author"`
Comments []Comment `json:"comments"`
}
type Author struct {
Id int `json:"id"`
Name string `json:"name"`
}
type Comment struct {
Id int `json:"id"`
Content string `json:"content"`
Author string `json:"author"`
}
func main() {
jsonFile, err := os.Open("post.json")
if err != nil {
fmt.Println("Error opening JSON file:", err)
return
}
defer jsonFile.Close()
decoder := json.NewDecoder(jsonFile)
for {
var post Post
err := decoder.Decode(&post)
if err == io.EOF {
break
}
if err != nil {
fmt.Println("Error decoding JSON:", err)
return
}
fmt.Println(post)
}
}
创建结构并向其填充数据
↓
结构封装为JSON数据
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
type Post struct {
Id int `json:"id"`
Content string `json:"content"`
Author Author `json:"author"`
Comments []Comment `json:"comments"`
}
type Author struct {
Id int `json:"id"`
Name string `json:"name"`
}
type Comment struct {
Id int `json:"id"`
Content string `json:"content"`
Author string `json:"author"`
}
func main() {
post := Post{
Id: 1,
Content: "Hello World!",
Author: Author{
Id: 2,
Name: "Sau Sheong",
},
Comments: []Comment{
Comment{
Id: 1,
Content: "Have a great day!",
Author: "Adam",
},
Comment{
Id: 2,
Content: "How are you today?",
Author: "Betty",
},
},
}
output, err := json.MarshalIndent(&post, "", "\t\t")
if err != nil {
fmt.Println("Error marshalling to JSON:", err)
return
}
err = ioutil.WriteFile("post.json", output, 0644)
if err != nil {
fmt.Println("Error writing JSON to file:", err)
return
}
}
创建结构并向其填充数据
↓
创建解码存储JSON的JSON
↓
创建编码JSON的编码器
↓
遍历JSON文件并用编码器将数据编码至结构
package main
import (
"encoding/json"
"fmt"
"io"
"os"
)
type Post struct {
Id int `json:"id"`
Content string `json:"content"`
Author Author `json:"author"`
Comments []Comment `json:"comments"`
}
type Author struct {
Id int `json:"id"`
Name string `json:"name"`
}
type Comment struct {
Id int `json:"id"`
Content string `json:"content"`
Author string `json:"author"`
}
func main() {
post := Post{
Id: 1,
Content: "Hello World!",
Author: Author{
Id: 2,
Name: "Sau Sheong",
},
Comments: []Comment{
Comment{
Id: 1,
Content: "Have a great day!",
Author: "Adam",
},
Comment{
Id: 2,
Content: "How are you today?",
Author: "Betty",
},
},
}
jsonFile, err := os.Create("post.json")
if err != nil {
fmt.Println("Error creating JSON file:", err)
return
}
defer jsonFile.Close()
jsonWriter := io.Writer(jsonFile)
encoder := json.NewEncoder(jsonWriter)
//指定前缀和缩进
encoder.SetIndent("", "\t")
err = encoder.Encode(&post)
if err != nil {
fmt.Println("Error encoding JSON to file:", err)
return
}
}
install.sql
drop database if exists gwp;
create database gwp;
drop user if exists gwp;
create user gwp with password 'gwp';
grant all privileges on database gwp to gwp;
setup.sql
drop table if exists posts;
create table posts (
id serial primary key,
content text,
author varchar(255)
);
//dropdb -h localhost -p 5433 -U gwp gwp
//dropuser -h localhost -p 5433 -U postgres gwp
psql -h localhost -p 5433 -U postgres -f D:\soft\PostgreSQL\install.sql
psql -h localhost -p 5433 -U gwd -d gwd -f F:\study\PostgreSQL\setup.sql
package main
import (
"database/sql"
_ "github.com/lib/pq"
)
var Db *sql.DB
// connect to the Db
func init() {
var err error
Db, err = sql.Open("postgres", "host=localhost port=5433 user=gwp dbname=gwp password=gwp sslmode=disable")
if err != nil {
panic(err)
}
err = Db.Ping()
if err != nil {
panic(err)
}
}
// Get a single post
func retrieve(id int) (post Post, err error) {
post = Post{}
err = Db.QueryRow("select id, content, author from posts where id = $1", id).Scan(&post.Id, &post.Content, &post.Author)
return
}
// Create a new post
func (post *Post) create() (err error) {
statement := "insert into posts (content, author) values ($1, $2) returning id"
stmt, err := Db.Prepare(statement)
if err != nil {
return
}
defer stmt.Close()
err = stmt.QueryRow(post.Content, post.Author).Scan(&post.Id)
return
}
// Update a post
func (post *Post) update() (err error) {
_, err = Db.Exec("update posts set content = $2, author = $3 where id = $1", post.Id, post.Content, post.Author)
return
}
// Delete a post
func (post *Post) delete() (err error) {
_, err = Db.Exec("delete from posts where id = $1", post.Id)
return
}
package main
import (
"encoding/json"
"net/http"
"path"
"strconv"
)
type Post struct {
Id int `json:"id"`
Content string `json:"content"`
Author string `json:"author"`
}
func main() {
server := http.Server{
Addr: ":8080",
}
http.HandleFunc("/post/", handleRequest)
server.ListenAndServe()
}
// main handler function
func handleRequest(w http.ResponseWriter, r *http.Request) {
var err error
switch r.Method {
case "GET":
err = handleGet(w, r)
case "POST":
err = handlePost(w, r)
case "PUT":
err = handlePut(w, r)
case "DELETE":
err = handleDelete(w, r)
}
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
// Retrieve a post
// GET /post/1
func handleGet(w http.ResponseWriter, r *http.Request) (err error) {
id, err := strconv.Atoi(path.Base(r.URL.Path))
if err != nil {
return
}
post, err := retrieve(id)
if err != nil {
return
}
output, err := json.MarshalIndent(&post, "", "\t\t")
if err != nil {
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(output)
return
}
// Create a post
// POST /post/
func handlePost(w http.ResponseWriter, r *http.Request) (err error) {
len := r.ContentLength
body := make([]byte, len)
r.Body.Read(body)
var post Post
json.Unmarshal(body, &post)
err = post.create()
if err != nil {
return
}
w.WriteHeader(200)
return
}
// Update a post
// PUT /post/1
func handlePut(w http.ResponseWriter, r *http.Request) (err error) {
id, err := strconv.Atoi(path.Base(r.URL.Path))
if err != nil {
return
}
post, err := retrieve(id)
if err != nil {
return
}
len := r.ContentLength
body := make([]byte, len)
r.Body.Read(body)
json.Unmarshal(body, &post)
err = post.update()
if err != nil {
return
}
w.WriteHeader(200)
return
}
// Delete a post
// DELETE /post/1
func handleDelete(w http.ResponseWriter, r *http.Request) (err error) {
id, err := strconv.Atoi(path.Base(r.URL.Path))
if err != nil {
return
}
post, err := retrieve(id)
if err != nil {
return
}
err = post.delete()
if err != nil {
return
}
w.WriteHeader(200)
return
}
//curl
//-i: 输出响应首部
//-X POST: 指定请求方法
//-H "Content-Type: application/json": 指定请求首部
//-d '{"content":"My first post","author":"Sau Sheong"}': POST数据
//script_create
curl -i -X POST -H "Content-Type: application/json" -d '{"content":"My first post","author":"Sau Sheong"}' http://127.0.0.1:8080/post/
psql -p 5433 -U gwp -d gwp -c "select * from posts;"
//script_retrieve
curl -i -X GET http://127.0.0.1:8080/post/1
//script_update
curl -i -X PUT -H "Content-Type: application/json" -d '{"content":"Updated post","author":"Sau Sheong"}' http://127.0.0.1:8080/post/1
psql -p 5433 -U gwp -d gwp -c "select * from posts;"
//script_delete
curl -i -X DELETE http://127.0.0.1:8080/post/1
psql -p 5433 -U gwp -d gwp -c "select * from posts;"