前面提到了Web应用和Web服务的区别,Web服务就是一个向其他软件程序提供服务的程序。本章将扩展这一定义,并展示如何使用Go语言来编写或使用Web服务。
1. 基于REST的Web服务
REST(Representational State Transfer,具象状态传输)是一种设计理念,用于设计那些通过标准的几个动作来操纵资源,并以此来进行相互交流的程序。
在OOP面向对象编程中,人们通过创建称为对象(object)的模型来表示事物,然后定义称为方法(method)的函数并将它们附着到模型之上。REST是以上思想的进化版,但它并不是把函数暴露(expose)为可调用的服务,而是以资源(resource)的名义把模型暴露出来,并允许人们通过少数几个称为动词的动作来操纵这些资源。
在使用HTTP协议实现REST服务时,URL将用于表示资源,而HTTP方法则会用作操纵资源的动词:
2. XML
下面介绍go语言是如何实现REST服务的。
2.1 分析XML
XML可以以结构化的形式表示数据,跟HTML一样,都是一种流行的标记语言。
在Go语言里面,用户首先需要将XML的分析结果存储到一些结构里面,然后通过访问这些结构来获取XML记录的数据。
- 创建一些用于存储XML数据的结构;
- 使用
xml.Unmarshal
将XML数据解封(unmarshal)到结构里面
下面举个例子:
首先创建一个XML文件
在这个XML文件中,包含了一个post结构,由id
决定,成员包括了纯粹文本的content
和以结构形式出现的author
,而author
由id
决定。
--- post
--- id
--- content
--- author
--- id
--- author
由此我们在程序中定义结构,用于表示数据:
Post
结构中每个字段的定义后面都带有一段使用反引号(`)包围的信息,这些信息被称为结构标签。
出于创建映射的需要,xml包要求被映射的结构以及结构包含的所有字段都必须是公开的,也就是,它们的名字必须以大写的英文字母开头。以上面展示的代码为例,结构的名字必须为Post而不能是post,至于字段的名字则必须为Content而不能是content。
XML使用的详细规则如下:
- 通过创建一个名字为
XMLName
、类型为xml.Name
的字段,可以将XML元素的名字存储在这个字段里面(在一般情况下,结构的名字就是元素的名字)。 - 有模式标志
- 通过创建一个与XML元素属性同名的字段,并使用
'xml:"<name>,attr"'
作为该字段的结构标签,可以将元素的<name>
属性的值存储到这个字段里面。 - 通过创建一个与XML元素标签同名的字段,并使用
'xml:",chardata"
‘作为该字段的结构标签,可以将XML元素的字符数据存储到这个字段里面。 - 通过定义一个任意名字的字段,并使用
'xml:",innerxml"
‘作为该字段的结构标签,可以将XML元素中的原始XML存储到这个字段里面。
- 通过创建一个与XML元素属性同名的字段,并使用
- 没有模式标志的结构字段将与同名的
XML
元素匹配。 - 使用
'xml:"a>b>c
“‘这样的结构标签可以在不指定树状结构的情况下直接获取指定的XML元素,其中a和b为中间元素,而c则是想要获取的节点元素。
我们按照规则,对结构逐一分析:
根据规则1:分析程序将XML文件中的元素名字post
存储到了Post
结构体的XMLName
字段里面。
根据规则2a:分析程序通过结构标签xml:"id,attr
“将XML文件中的id
属性的值存储到了Post
结构的Id
字段里面。
根据规则3:分析程序通过结构标签'xml:"content"'
将content
子元素包含的字符数据存储到了Post
结构的Content
字段里面。
根据规则2c:分析程序定义了一个Xml字段,并使用'xml:",in-nerxml"'
作为该字段的结构标签,以此来获得被post
元素包含的原始XML:
根据规则3,子元素author
拥有id
属性,并且包含字符数据SauSheong
,为了正确地构建映射,分析程序专门定义了Author
结构:
运行后输出:
{{ post} 1 Hello World! {2 Sau Sheong}
<content>Hello World!</content>
<author id="2">Sau Sheong</author>
}
下面展示规则4的使用:
新添加代码定义了一个名为comments
的XML子元素,并且这个元素本身也包含多个comment
子元素。正常来说分析程序需要获取帖子的评论列表,但为此专门创建一个Comments
结构可能会显得有些小题大做了。为了简化实现代码,分析程序将根据规则56对comments
这个XML子元素进行跳跃式访问。
首先修改Post结构:
通过结构标签'xml:"comments>comment"'
将这个字段映射至名为comment
的XML子元素。根据规则5,这一结构标签将允许分析程序跳过XML中的comments
元素,直接访问comment
子元素。
Comment
结构和Post
结构非常相似,它的具体定义如下:
这种做法虽然能够很好地处理体积较小的XML文件,但是却无法高效地处理以流(stream)方式传输的XML文件以及体积较大的XML文件。为了解决这个问题,我们需要使用Decoder
结构来代替Unmarshal
函数,通过手动解码XML元素的方式来解封XML数据,这个过程如图所示。
示例如下:
虽然这段代码只演示了如何解码comment
元素,但这种解码方式同样可以应用于XML文件中的其他元素。这个新的分析程序会通过Decoder
结构,一个元素接一个元素地对XML进行解码,而不是像之前那样,使用Unmarshal
函数一次将整个XML解封为字符串。
2.2 创建XML
go中将结构封装为XML用到了函数marshal
。
装程序首先需要创建表示帖子的post
结构,并向结构里面填充数据,然后只要调用Marshal
函数,就可以根据Post
结构创建相应的XML了
这样输出的XML是没有换行的,格式不太好,可以使用MarshalIndent
函数。
MarshalIndent
函数还接受两个额外的参数,这两个参数分别用于指定添加到每个输出行前面的前缀以及缩进,其中缩进的数量会随着元素的嵌套层次增加而增加。
但是这段输出还没有添加XML声明,我们需要手动添加:
同理,也可以手动使用Encoder
编码:
3. JSON
3.1 分析JSON
JSON(JavaScript Object Notation)是衍生自JavaScript语言的一种轻量级的文本数据格式,这种格式的主要设计理念是既能够轻易地被人类读懂,又能够简单地被机器读取。
JSON分析的过程和XML非常相似:
- 创建一些用于包含JSON数据的结构;
- 通过
json.Unmarshal
函数,把JSON数据解封到结构里面。
跟映射XML相比,把结构映射至JSON要简单得多,后者只有一条通用的规则:对于名字为<name>
的JSON键,用户只需要在结构里创建一个任意名字的字段,并将该字段的结构标签设置为'json:"<name>"'
,就可以把JSON键<name>
的值存储到这个字段里面。
JSON文件:
go的json分析程序:
为了将JSON键id
的值映射到Post
结构的Id
字段,程序将该字段的结构标签设置成了'json:"id"'
,这种设置基本上就是将结构映射至JSON数据所需完成的全部工作。
当然也可以用Decoder
手动地将JSON数据解码到结构里面。
3.2 创建JSON
创建过程和XML类似,首先创建结构,然后调用函数将其封装为JSON数据。