• 《Go Web 编程》之第7章 Go Web服务


    第7章 Go Web服务

    Go Web服务就是向其它软件程序提供服务的程序。

    7.1 Web服务简介

    Web服务的终端用户(end user)不是人类,而是软件程序,通过HTTP进行通信。

    SOAP优点:
    安全、健壮;
    能使用WSDL(web service description language)进行明确描述。

    SOAP缺点:
    笨重、复杂;
    XML报文冗长,难以调试;
    额外资源损耗而无法高效运行。

    REST优点:
    非结构而是设计理念,灵活简单;
    基于JSON简单数据格式而非XML,运行高效。

    SOAP,功能驱动,用于实现内部应用的企业集成(enterprise integration);
    REST,数据驱动,关注资源,HTTP方法是操作资源的动词,用于服务外部和第三方的开发者。

    7.2 基于SOAP的Web服务简介

    Simple Object Access Protocol(简单对象访问协议,名不符实)。
    SOAP高度结构化,严格定义,用于传输数据的XML非常复杂。

    大多数基于SOAP的Web服务通过HTTP的POST方法(Content-Type:application/soap+xml)发送SOAP报文。
    WSDL报文冗长。

    7.3 基于REST的Web服务简介

    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)动作用作资源的属性。

    7.3.1 将动作转换为资源

    POST /user/456/activation HTTP/1.1
    {"date": "2015-05-15T13:05:05Z"}
    
    • 1
    • 2

    给用户456的activation资源附加了日期属性。

    7.3.2 将动作转换为资源的属性

    PATCH /user/456 HTTP/1.1
    {"active": "true"}
    
    • 1
    • 2

    将用户456资源的active属性设置为true。

    7.4 通过Go分析和创建XML

    7.4.1 分析XML

    7.4.1.1 一次性读取XML

    (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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    7.4.1.2 流方式读取XML

    高效地处理以流(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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    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)
    			}
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    7.4.2 创建XML

    7.4.2.1 结构体写入XML

    将结构封装(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
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    7.4.2.2 编码器写入XML

    创建结构并填充数据

    创建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
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    7.5 通过Go分析和创建JSON

    JSON(JavaScritp Object Notation),衍生自JavaScript语言的一种轻量级的文本数据格式,设计理念是能被人类读懂,能被机器简单读取。

    7.5.1 分析JSON

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    7.5.1.1 一次性读取JSON

    (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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    7.5.1.2 流式读取JSON

    创建存储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)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    7.5.2 创建JSON

    7.5.2.1 结构体生成JSON

    创建结构并向其填充数据

    结构封装为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
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    7.5.2.2 编码器生成JSON

    创建结构并向其填充数据

    创建解码存储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
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    7.6 创建Web服务

    • 创建用户和数据库

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 创建表格

    setup.sql

    drop table if exists posts;
    
    create table posts (
      id      serial primary key,
      content text,
      author  varchar(255)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 相关脚本
    //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
    
    • 1
    • 2
    • 3
    • 4
    • 数据操作的Go代码
      data.go
    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 服务器
      server.go
    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 测试脚本
    //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;"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    java部分常见错误示例
    电子表格软件怎么选?
    天地图WMTS地图瓦片下载
    Camtasia mac版怎么加字幕 Camtasia mac版怎么打马赛克
    Git概述
    switchhosts怎么配置host?
    Java继承的三个特点
    SpringCloud使用Zookeeper作为服务注册发现中心
    记录一次系统蓝屏,错误代码WHEA_UNCORRECTABLE_ERROR
    React报错之React hook 'useState' cannot be called in a class component
  • 原文地址:https://blog.csdn.net/oqqyx1234567/article/details/126737453