goa-2-简单实现RESTful 微服务 | KaiQ.Gu|KerwinKoo Blog
使用Goa
微服务环境搭建
首先目录文件一定要创建在$GOPATH/src下,假设做一个cellar服务,首先创建目录$GOPATH/src/cellar, 然后在此目录下创建子文件夹design和文件design/design.go。
示例代码
source code list
在文件design/design.go中写入代码:
1 | package design |
代码解读
design包名不限制,什么名字都可。API函数,用于声明API,第一个参数为API名字,第二个参数是匿名函数,用于定义该API的基本属性。Resource用于声明服务资源(resource),因为Goa是RESTful设计理念,source在REST中囊括了API所能访问的所有属性,参数即地址,地址亦参数,http的GET、POST等也属于资源范畴。因此函数Resource是Goa设计对象的主体。在上面的代码示例中,函数Resource用于声明一个名为“bottle”的资源,同样的,在匿名函数中对该资源进行属性、行为声明。在
Resource函数中,Action可以定义该资源所提供的行为,Action函数必须在Resource函数中定义。Action同样是一个行为名称+一个匿名函数,在匿名函数中声明了该行为的描述、HTTP method、参数及返回值等属性。其中,HTTP method(代码中的Routing声明)中的路径需使用通配符(:变量名)。承接参数类型定义使用Params,如果该Action不止一个参数,需要多次调用Params对每个参数进行承接声明。同一个Resource函数中,可以定义多个Action函数。Goa用代码描述设计,用设计生成代码。Goa设计的最终端点是Action,并通过函数Action描述。Goa定义了基本的response模板,这里默认返回OK(200)。当资源访问成功后,可以将访问变量提交给我们最后定义的
Media,Media通过函数MediaType定义,Media的结构组成,则是匿名函数Attributes中定义。
生成代码
通过goagen将设计生成代码:
goagen bootstrap -d cellar/design代码目录结构
在当前工作目录下会生成如下文件结构:
1 | app |
bottle.go为主结构代码文件,业务逻辑需要在此进行开发实现。当之后再做设计修改,运行goagen bootstrap命令后,该文件不会改变,只改变app、client和swagger下的文件。通过goagen命令行参数可以指定app名定向生成代码,比如我们需要修改main.go等文件时,可以使用goagen main -d命令。
目录功能说明
app目录中的代码是API的胶水代码(脚手架代码),用于实现HTTP底层功能,包括信息接收管理,信息反馈模板,底层路由分发,以及goa定义的媒介结构体。如果没有特殊要求,这个目录下的代码文件系自动生成,不需要认为修改。client包含一个客户端的简单实现,支持命令行参数,可以进行简单的server request测试。swagger目录中存放swagger规范的API说明文件(swagger.json),通过访问swagger可以查看API说明。主目录src下的
main.go和bottle.go实现简单的server。
app包:
controllers.go:控制接口。在设计过程中,每一个resource都有一个接口定义。例子中的接口定义如下:
1 | // BottleController is the controller interface for the Bottle actions. |
因此接口需要一个goa.Controller,需要实现Show(*ShowBottleContext) error函数,后者在bottle.go中实现。
controllers.go还实现了向server mount的功能,将其自身API挂载到server中。
contexts.go:内容结构体的实现。hrefs.go:提供资源信息描述功能。media_types.go:实现应答数据结构体类型的定义。user_types.go:定义通过 "Type" 设计的结构体。
完善代码
仍旧是上面代码的例子。Goa已经实现了一个基本且功能全面的框架,剩下的需要我们实现bottle控制器。回到之前介绍的BottleController接口(定义在controllers.go文件中)和ShowBottleContext结构体(定义在app/contexts.go文件中):
1 | // BottleController is the controller interface for the Bottle actions. |
1 | // ShowBottleContext provides the bottle show action context. |
其中BottleID int是我们在之前design时设定的。
次文件包含另外两个函数的实现:
1 | // NotFound sends a HTTP response with status code 404. |
上面两个函数仍是Template,供后续开发或bottle.go文件调用。
利用上面的基础框架,我们需要实现文件bottle.go的具体功能。当前的API访问时仅返回一个200,我们需要根据具体业务来实现API功能函数。
用以下代码替换show方法(注意引用fmt包):
1 | // Show implements the "show" action of the "bottles" controller. |
上面代码与官方示例代码略有不同,但本质相同。官方代码有赋值错误,将value赋值给了point,因此我做了过程上的微小处理。
API实现后,会在main.go中进行挂载。
注意:main中进行挂载的API不能有路由冲突,比如API资源/foo/:id和/foo/:fooID/bar/:id不能同时存在,需要将第一个修改为/foo/:fooID
编译测试
在当前目录进行编译并启动server:
go build -o cellar_server./cellar_server官方示例中是go build -o cellar,但生成过程中本身已存在cellar目录,所有会出现编译错误。这里改成cellar_server。
编译过程中出现其他问题,移步到我上一篇关于Goa的日志。
运行后,打印启动日志:
1 | INFO[01-21|18:22:11] mount app=API ctrl=Bottle action=Show route="GET /bottles/:bottleID" |
从日志中看,路由中挂载了两个API,分别为Bottle和swagger,方法均为GET。
curl工具测试
可以通过curl工具进行测试:
curl -i localhost:8080/bottles/1234curl -i localhost:8080/bottles/0第二个是测试之前我们实现的功能:
1 | if ctx.BottleID == 0 { |
swagger测试:
curl -i localhost:8080/swagger.jsonclient CLI测试
Goa同时生成CLI测试客户端:
$ cd client/cellar-cli$ go build -o cellar-cli$ ./cellar-cli --dump show bottle /bottles/1234效果与curl大体相同,多了一个href的显示:
{"href":"/bottles/1234","id":1234,"name":"Bottle #1234"}通过这个例子,可以体会Goa以设计为基础的HTTP API开发。