Goa Media

发布于 2024-09-01 17:36:38 字数 4169 浏览 20 评论 0

使用 Goa 的 Media 之前,首先得明白其存在的意义。

当终端访问我们的 HTTP API 时,应有一个双方约定好的信息格式载体,我们以常用的 JSON 为例。 Response body 信息为 JSON,同时 server 端反馈的信息也应该是 JSON。在 Go 代码里,JSON 的本质是一个 type 为 string 的 key-value 的 map,代码表述为:

map[string]interface{}

其中 interface 接受各种类型。

当我们需要实现这么一个 map 时,最规范也是最常用的办法,就是将此段 JSON 的模板结构体直接赋值转换为 map。

因此,go 生成 API-JSON 返回信息的基本流程为:

  • 1.定义结构体&定义该结构体对应的 JSON 模板
  • 2.为结构体变量赋值
  • 3.将结构体变量映射到 JSON 模板中,生成 JSON 型 map
  • 4.将此 map(已成为 JSON)返回给终端

除了 第 2 步 需要人为的写代码赋值外,Goa 的 MediaType 均可以在 Design 过程中指定关键字来实现。

基于官方的 Test code 举个栗子:

design.go 中关于 API resource: bottle 的 testcode 解读:

var _ = Resource("bottle", func() { //定义资源,资源名为 bottle
	BasePath("/bottles")
	DefaultMedia(BottleMedia) //指定 generated code 中默认使用的 Media
	Action("show", func() {
		Description("Retrieve bottle with given id")
		Routing(GET("/:bottleID"))
		Params(func() {
			Param("bottleID", Integer, "Bottle ID")
		})
		Response(OK)
		Response(NotFound)
	})
})

var BottleMedia = MediaType("application/vnd.goa.example.bottle+json", func() {
	Description("A bottle of wine")
	TypeName("BottleMedia") //给 Media 重命名
	Attributes(func() {
		Attribute("id", Integer, "Unique bottle ID") //参数分别为 JSON-key,type,描述
		Attribute("href", String, "API href for making requests on the bottle")
		Attribute("name", String, "Name of wine")
	})

	View("default", func() { //定义好 Media 后,必须要指定一个 default 的 View
		Attribute("id") //在这个 View 中暴露出的属性
		Attribute("href")
		Attribute("name")
	})
})

代码说明:

  • DefaultMedia(BottleMedia) 中的变量不一定非得是在 View 中指定为“default”的 Media,而是说要使用哪个 Media(Test code 里只定义了一个名为 default 的 media)。如果没有这句代码,默认生成的 Show method 里没有 media 的框架(可以手动添加)。
  • TypeName("BottleMedia") 该行代码的作用是将 Media 重命名。如果没有这一行,Goa 会根据 MediaType 函数的第一个参数生成 Media 名(即结构体名字),上面的例子中,如果去掉该行,生成的 Media 名为 GoaExampleBottle
  • View 可以定义多个,在生成的代码中,决定使用哪个 JSON 模板做 Media 的载体。但 View 中必须要有一个名为 default 的定义,当没有指定 view 时,系统默认暴露 default。只要有 media,就要有个 default 的 view。
  • 关于 MediaView 的关系。View 是 Media 的子集,REST API 中一个资源可以有很多属性,但并不是每个该资源的 HTTP 请求都应反馈整个属性 list,而是根据不同的请求地址,返回不同需求的资源信息,这种方式可以在 Design 过程中通过 View 实现。

上面的 design 代码生成的次代代码(media_types.go) 如下:

// A bottle of wine
// Identifier: application/vnd.goa.example.bottle+json
type BottleMedia struct {
	// API href for making requests on the bottle
	Href *string `json:"href,omitempty"`
	// Unique bottle ID
	ID *int `json:"id,omitempty"`
	// Name of wine
	Name *string `json:"name,omitempty"`
}

// Dump produces raw data from an instance of BottleMedia running all the
// validations. See LoadBottleMedia for the definition of raw data.
func (mt *BottleMedia) Dump() (res map[string]interface{}, err error) {
	res, err = MarshalBottleMedia(mt, err)
	return
}

// MarshalBottleMedia validates and renders an instance of BottleMedia into a interface{}
// using view "default".
func MarshalBottleMedia(source *BottleMedia, inErr error) (target map[string]interface{}, err error) {
	err = inErr
	tmp23 := map[string]interface{}{
		"href": source.Href,
		"id":   source.ID,
		"name": source.Name,
	}
	target = tmp23
	return
}

从代码中可以看出,当我们需要给该 Media 赋值时,需要将资源(BottleMedia 型的结构体指针)传值给 MarshalBottleMedia 。该资源在对应的 contexts.go 中进行定义。

生成的 Action Func 代码(bottle.go)如下:

// Show runs the show action.
func (c *BottleController) Show(ctx *app.ShowBottleContext) error {
	res := &app.BottleMedia{}
	return ctx.OK(res)
}

可以看到定义的 res 变量即为我们要的 BottleMedia 型的结构体指针,而函数 MarshalBottleMedia 进行多重封装后在 return ctx.OK(res) 这一句中被调用,因此上面讲的第二步,即是我们需要做的修改,根据业务,将信息写入变量 res ,然后发出。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

上一篇:

下一篇:

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

0 文章
0 评论
22 人气
更多

推荐作者

謌踐踏愛綪

文章 0 评论 0

开始看清了

文章 0 评论 0

高速公鹿

文章 0 评论 0

alipaysp_PLnULTzf66

文章 0 评论 0

热情消退

文章 0 评论 0

白色月光

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文